PSGSuite.psm1
Param ( [parameter(Position = 0,ValueFromRemainingArguments = $true)] [AllowNull()] [Byte[]] $EncryptionKey = $null, [parameter(Position = 1)] [AllowNull()] [String] $ConfigName ) $ModuleRoot = $PSScriptRoot New-Variable -Name PSGSuiteKey -Value $EncryptionKey -Scope Global -Force function Convert-Base64 { [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0)] [ValidateSet("NormalString","Base64String","WebSafeBase64String")] [ValidateScript( {if ($_ -eq $To) { throw "The 'From' parameter must not be the same as the 'To' parameter" } else { $true }})] [String] $From, [parameter(Mandatory = $true,Position = 1)] [ValidateSet("NormalString","Base64String","WebSafeBase64String")] [ValidateScript( {if ($_ -eq $From) { throw "The 'To' parameter must not be the same as the 'From' parameter" } else { $true }})] [String] $To, [parameter(Mandatory = $true,Position = 2,ValueFromPipeline = $true)] [String] $String, [parameter(Mandatory = $false)] [String] $OutFile ) if ($From -eq "NormalString") { $String = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($String)) } elseif ($From -eq "WebSafeBase64String") { $String = $String.Replace('_', '/').Replace('-', '+').Replace('|','=') switch ($String.Length % 4) { 2 { $String += "==" } 3 { $String += "=" } } } if ($To -eq "NormalString") { $String = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($String)) } elseif ($To -eq "WebSafeBase64String") { $String = $String.TrimEnd("=").Replace('+', '-').Replace('/', '_'); } if ($OutFile) { $String | Set-Content $OutFile -Force } else { return $String } } function Convert-DateToEpoch { Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true)] [datetime] $Date ) Begin { $UnixEpoch = [timezone]::CurrentTimeZone.ToLocalTime([datetime]'1/1/1970') } Process { $result = (("$(($Date - $UnixEpoch).TotalMilliseconds)" -split "\.") -split "\,")[0] } End { return $result } } function Convert-EpochToDate { Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true)] [string] $EpochString ) Begin { $UnixEpoch = [timezone]::CurrentTimeZone.ToLocalTime([datetime]'1/1/1970') } Process { try { $result = $UnixEpoch.AddSeconds($EpochString) } catch { try { $result = $UnixEpoch.AddMilliseconds($EpochString) } catch { $result = $UnixEpoch.AddTicks($EpochString) } } } End { return $result } } function Get-MimeType { Param ( [parameter(Mandatory=$true, ValueFromPipeline=$true,Position = 0)] [System.IO.FileInfo] $File ) $mimeHash = @{ arj = 'application/arj' bmp = 'image/bmp' cab = 'application/cab' csv = 'text/plain' doc = 'application/msword' gif = 'image/gif' htm = 'text/html' html = 'text/html' jpg = 'image/jpeg' js = 'text/js' log = 'text/plain' md = 'text/plain' mp3 = 'audio/mpeg' ods = 'application/vnd.oasis.opendocument.spreadsheet' pdf = 'application/pdf' php = 'application/x-httpd-php' png = 'image/png' rar = 'application/rar' swf = 'application/x-shockwave-flash' tar = 'application/tar' tmpl = 'text/plain' txt = 'text/plain' xls = 'application/vnd.ms-excel' xlsx = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' xml = 'text/xml' zip = 'application/zip' } if ($File.PSIsContainer) { 'application/vnd.google-apps.folder' } elseif ($mime = $mimeHash["$($file.Extension.TrimStart('.'))"]) { $mime } else { 'application/octet-stream' } } function Import-GoogleSDK { [CmdletBinding()] Param() Process { $lib = Resolve-Path "$($script:ModuleRoot)\lib" $refs = @() $sdkPath = if ($PSVersionTable.PSVersion.Major -lt 6) { Write-Verbose "Importing the SDK's for net45" "$lib\net45" } else { Write-Verbose "Importing the SDK's for netstandard1.3" "$lib\netstandard1.3" } Get-ChildItem $sdkPath -Filter "*.dll" | Where-Object {$_.Name -notin $refs} | ForEach-Object { $sdk = $_.Name try { Add-Type -Path $_.FullName -ErrorAction Stop } catch [System.Reflection.ReflectionTypeLoadException] { Write-Host "Message: $($_.Exception.Message)" Write-Host "StackTrace: $($_.Exception.StackTrace)" Write-Host "LoaderExceptions: $($_.Exception.LoaderExceptions)" } catch { Write-Error "$($sdk): $($_.Exception.Message)" } } } } function Import-SpecificConfiguration { <# .Synopsis Import the full, layered configuration for the module. Allows for specification of scoped module to load, in case different scopes have different encryption levels .Description Imports the DefaultPath Configuration file, and then imports the Machine, Roaming (enterprise), and local config files, if they exist. Each configuration file is layered on top of the one before (so only needs to set values which are different) .Example $Configuration = Import-Configuration This example shows how to use Import-Configuration in your module to load the cached data .Example $Configuration = Get-Module Configuration | Import-Configuration This example shows how to use Import-Configuration in your module to load data cached for another module #> [CmdletBinding(DefaultParameterSetName = '__CallStack')] param( # A callstack. You should not ever pass this. # It is used to calculate the defaults for all the other parameters. [Parameter(ParameterSetName = "__CallStack")] [System.Management.Automation.CallStackFrame[]]$CallStack = $(Get-PSCallStack), # The Module you're importing configuration for [Parameter(ParameterSetName = "__ModuleInfo", ValueFromPipeline = $true)] [System.Management.Automation.PSModuleInfo]$Module = $( $mi = ($CallStack)[0].InvocationInfo.MyCommand.Module if ($mi -and $mi.ExportedCommands.Count -eq 0) { if ($mi2 = Get-Module $mi.ModuleBase -ListAvailable | Where-Object Name -eq $mi.Name | Where-Object ExportedCommands | Select-Object -First 1) { return $mi2 } } return $mi ), # An optional module qualifier (by default, this is blank) [Parameter(ParameterSetName = "ManualOverride", Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [Alias("Author")] [String]$CompanyName = $( if ($Module) { $Name = $Module.CompanyName -replace "[$([Regex]::Escape(-join[IO.Path]::GetInvalidFileNameChars()))]","_" if ($Name -eq "Unknown" -or -not $Name) { $Name = $Module.Author if ($Name -eq "Unknown" -or -not $Name) { $Name = "AnonymousModules" } } $Name } else { "AnonymousScripts" } ), # The name of the module or script # Will be used in the returned storage path [Parameter(ParameterSetName = "ManualOverride", Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [String]$Name = $(if ($Module) { $Module.Name }), # The full path (including file name) of a default Configuration.psd1 file # By default, this is expected to be in the same folder as your module manifest, or adjacent to your script file [Parameter(ParameterSetName = "ManualOverride", ValueFromPipelineByPropertyName = $true)] [Alias("ModuleBase")] [String]$DefaultPath = $(if ($Module) { Join-Path $Module.ModuleBase Configuration.psd1 }), [Parameter(ParameterSetName = "Path",Mandatory = $true)] [ValidateScript( {Test-Path $_})] [string]$Path, [Parameter(Mandatory = $false)] [ValidateSet("User", "Machine", "Enterprise", $null)] [string]$Scope = $Script:ConfigScope, # The version for saved settings -- if set, will be used in the returned path # NOTE: this is *never* calculated, if you use version numbers, you must manage them on your own [Version]$Version, # If set (and PowerShell version 4 or later) preserve the file order of configuration # This results in the output being an OrderedDictionary instead of Hashtable [Switch]$Ordered ) begin { Write-Verbose "Module Name $Name" } process { if (!$Name) { throw "Could not determine the configuration name. When you are not calling Import-Configuration from a module, you must specify the -Author and -Name parameter" } if (Test-Path $DefaultPath -Type Container) { $DefaultPath = Join-Path $DefaultPath Configuration.psd1 } $Configuration = if (Test-Path $DefaultPath) { Import-Metadata $DefaultPath -ErrorAction Ignore -Ordered:$Ordered Write-Verbose "Default config found: $DefaultPath" } else { @{} } $Parameters = @{ CompanyName = $CompanyName Name = $Name } if ($Version) { $Parameters.Version = $Version } if ($Path) { Write-Verbose "Importing configuration from specific path: $Path" Import-Metadata "$(Resolve-Path $Path)" -ErrorAction Ignore -Ordered:$Ordered } else { if (!$Scope -or $Scope -eq "Machine") { $MachinePath = Get-StoragePath @Parameters -Scope Machine $MachinePath = Join-Path $MachinePath Configuration.psd1 $Machine = if (Test-Path $MachinePath) { Import-Metadata $MachinePath -ErrorAction Ignore -Ordered:$Ordered Write-Verbose "Machine config found: $MachinePath" } else { @{} } } if (!$Scope -or $Scope -eq "Enterprise") { $EnterprisePath = Get-StoragePath @Parameters -Scope Enterprise $EnterprisePath = Join-Path $EnterprisePath Configuration.psd1 $Enterprise = if (Test-Path $EnterprisePath) { Import-Metadata $EnterprisePath -ErrorAction Ignore -Ordered:$Ordered Write-Verbose "Enterprise config found: $EnterprisePath" } else { @{} } } if (!$Scope -or $Scope -eq "User") { $LocalUserPath = Get-StoragePath @Parameters -Scope User $LocalUserPath = Join-Path $LocalUserPath Configuration.psd1 $LocalUser = if (Test-Path $LocalUserPath) { Import-Metadata $LocalUserPath -ErrorAction Ignore -Ordered:$Ordered Write-Verbose "LocalUser config found: $LocalUserPath" } else { @{} } } switch ($Scope) { Machine { Write-Verbose "Importing configuration at scope: $Scope" $Configuration | Update-Object $Machine } Enterprise { Write-Verbose "Importing configuration at scope: $Scope" $Configuration | Update-Object $Enterprise } User { Write-Verbose "Importing configuration at scope: $Scope" $Configuration | Update-Object $LocalUser } Default { Write-Verbose "Importing layered configuration" $Configuration | Update-Object $Machine | Update-Object $Enterprise | Update-Object $LocalUser } } } } } function New-MimeMessage { [cmdletbinding()] Param ( [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string[]] $To, [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String] $From, [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Subject, [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Body, [parameter(Mandatory = $false)] [string[]] $CC, [parameter(Mandatory = $false)] [string[]] $BCC, [parameter(Mandatory = $false)] [ValidateScript( {Test-Path $_})] [string[]] $Attachment, [parameter(Mandatory = $false)] [switch] $BodyAsHtml, [parameter(Mandatory = $false)] [switch] $ReturnConstructedMessage ) $message = [MimeKit.MimeMessage]::new() $message.From.Add($From) $message.Subject = $Subject foreach ($T in $To) { $message.To.Add($T) } if ($CC) { foreach ($C in $CC) { $message.Cc.Add($C) } } if ($BCC) { foreach ($B in $BCC) { $message.Bcc.Add($B) } } if ($BodyAsHtml) { $TextPart = [MimeKit.TextPart]::new("html") } else { $TextPart = [MimeKit.TextPart]::new("plain") } $TextPart.Text = $Body if ($Attachment) { [System.Reflection.Assembly]::LoadWithPartialName('System.IO') | Out-Null $Multipart = [MimeKit.Multipart]::new("mixed") $Multipart.Add($TextPart) foreach ($Attach in $Attachment) { $MimeType = (Get-MimeType -File $Attach) -split "/" $MimePart = [MimeKit.MimePart]::new($MimeType[0], $MimeType[1]) $MimePart.ContentObject = [MimeKit.ContentObject]::new([IO.File]::OpenRead($Attach), [MimeKit.ContentEncoding]::Default) $MimePart.ContentDisposition = [MimeKit.ContentDisposition]::new([MimeKit.ContentDisposition]::Attachment) $MimePart.ContentTransferEncoding = [MimeKit.ContentEncoding]::Base64 $MimePart.FileName = [IO.Path]::GetFileName($Attach) $Multipart.Add($MimePart) } $message.Body = $Multipart } else { $message.Body = $TextPart } if ($ReturnConstructedMessage) { return $message.ToString() } else { return $message } } function Out-TruncatedString { [CmdletBinding()] param ( [Parameter(Position = 0, ValueFromPipeline = $true)] [ValidateNotNullOrEmpty()] [string] $String, [Parameter(Position = 1)] [ValidateRange(0,[int32]::MaxValue)] [int] $Length = 0 ) $outString = $String if ($Length -gt 0) { if ($String.Length -gt $Length) { $outString = $String.Substring(0,($Length - 3)) + '...' } } Write-Output $outString } function Read-MimeMessage { [cmdletbinding(DefaultParameterSetName = "String")] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ParameterSetName = "String")] [ValidateNotNullOrEmpty()] [string[]] $String, [parameter(Mandatory = $true,ValueFromPipeline = $true,ParameterSetName = "EmlFile")] [ValidateScript( {Test-Path $_})] [string[]] $EmlFile ) Process { switch ($PSCmdlet.ParameterSetName) { String { foreach ($str in $String) { $stream = [System.IO.MemoryStream]::new([Text.Encoding]::UTF8.GetBytes($str)) [MimeKit.MimeMessage]::Load($stream) $stream.Dispose() } } EmlFile { foreach ($str in $EmlFile) { [MimeKit.MimeMessage]::Load($str) } } } } } function Read-Prompt { [CmdletBinding()] Param ( [parameter(Mandatory = $true,Position = 0)] $Options, [parameter(Mandatory = $false)] [String] $Title = "Picky Choosy Time", [parameter(Mandatory = $false)] [String] $Message = "Which do you prefer?", [parameter(Mandatory = $false)] [Int] $Default = 0 ) Process { $opt = @() foreach ($option in $Options) { switch ($option.GetType().Name) { Hashtable { foreach ($key in $option.Keys) { $opt += New-Object System.Management.Automation.Host.ChoiceDescription "$($key)","$($option[$key])" } } String { $opt += New-Object System.Management.Automation.Host.ChoiceDescription "$option",$null } } } $choices = [System.Management.Automation.Host.ChoiceDescription[]] $opt $answer = $host.ui.PromptForChoice($Title, $Message, $choices, $Default) $choices[$answer].Label -replace "&" } } function Test-FileLock { param ( [parameter(Mandatory = $false)] [string] $Path ) if ($Path) { $oFile = New-Object System.IO.FileInfo $Path if ((Test-Path -Path $Path) -eq $false) { return $false } try { $oStream = $oFile.Open([System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None) if ($oStream) { $oStream.Close() } return $false } catch { return $true } } } function ThrowTerm { Param ( [parameter(Mandatory = $true,Position = 0)] [String] $Message ) New-Object System.Management.Automation.ErrorRecord( (New-Object Exception $Message), 'PowerShell.Module.Error', [System.Management.Automation.ErrorCategory]::OperationStopped, $null ) } function Write-InlineProgress { <# .SYNOPSIS Display an inline progress bar in the PowerShell console. .DESCRIPTION Display an inline progress bar in the PowerShell console. .NOTES Be sure to always call the function with either the -Stop or -Completed switch after the progress bar is finished. Be sure to NOT output anything while the progress bar is updating - or it WILL "break"! This function will not work when run in PowerShell ISE. Author: ÃËœyvind Kallstad Date: 28.04.2016 Version: 1.0 .LINK https://communary.wordpress.com/ #> [CmdletBinding(DefaultParameterSetName = 'normal')] param ( # Describe the activity being performed. [Parameter(Position = 0, ParameterSetName = 'normal')] [Parameter(ParameterSetName = 'completed')] [string] $Activity, # Minimum padding for the activity text. When set to 0 (default) the size of the activity text # is automatically adjusted based on the window width. [Parameter(ParameterSetName = 'normal')] [Parameter(ParameterSetName = 'completed')] [ValidateRange(0,[int]::MaxValue)] [int] $ActivityPadding = 0, # Display seconds remaining. [Parameter(ParameterSetName = 'normal')] [Parameter(ParameterSetName = 'completed')] [ValidateRange(0,[int]::MaxValue)] [int] $SecondsRemaining, # Display seconds elapsed. [Parameter(ParameterSetName = 'normal')] [Parameter(ParameterSetName = 'completed')] [ValidateRange(0,[int]::MaxValue)] [int] $SecondsElapsed, # Define the percent complete value for the progress bar. [Parameter(ParameterSetName = 'normal')] [ValidateRange(0,100)] [int] $PercentComplete, # Display the percent complete. # Note! If the window width is below 40 the percent value will not be displayed. [Parameter(ParameterSetName = 'normal')] [Parameter(ParameterSetName = 'completed')] [switch] $ShowPercent = $true, # Stop without any update to the progress bar. [Parameter(ParameterSetName = 'stop')] [switch] $Stop, # Output last progress result. [Parameter(ParameterSetName = 'stop')] [switch] $OutputLastProgress, # Stop the progress bar with a final update. [Parameter(ParameterSetName = 'completed')] [switch] $Completed, # Customize the progress character. [Parameter(ParameterSetName = 'normal')] [Parameter(ParameterSetName = 'completed')] [ValidateLength(1,1)] [ValidateNotNull()] [string] $ProgressCharacter = '#', # Customize the progress fill character. [Parameter(ParameterSetName = 'normal')] [Parameter(ParameterSetName = 'completed')] [ValidateLength(1,1)] [ValidateNotNull()] [string] $ProgressFillCharacter = '#', # Customize the fill character. [Parameter(ParameterSetName = 'normal')] [Parameter(ParameterSetName = 'completed')] [ValidateLength(1,1)] [ValidateNotNull()] [string] $ProgressFill = '.', # Customize the bracket before the progress bar. [Parameter(ParameterSetName = 'normal')] [Parameter(ParameterSetName = 'completed')] [ValidateLength(0,1)] [string] $BarBracketStart = '[', # Customize the bracket after the progress bar. [Parameter(ParameterSetName = 'normal')] [Parameter(ParameterSetName = 'completed')] [ValidateLength(0,1)] [string] $BarBracketEnd = ']', # Use Write-Output instead of Console.Write # If you want to support transcripts, you need to use this parameter on the last update of the # progress bar so that it will be written to the transcript file. The same goes for any error handling if # you want the last status of the progress bar to be written to the transcript. [Parameter(ParameterSetName = 'normal')] [Parameter(ParameterSetName = 'completed')] [switch] $UseWriteOutput ) # this function only works when run from the console if ($Host.Name -notlike '*ISE*') { if ($Stop) { if ($OutputLastProgress) { Write-Host (($script:lastProgressString).ToString()) -NoNewline } else { Remove-Variable -Name 'lastProgressString' -Scope 'Script' -ErrorAction SilentlyContinue [console]::WriteLine() } [console]::CursorVisible = $true } else { if ($Completed) { $PercentComplete = 100 if ($PSBoundParameters.ContainsKey('SecondsRemaining')) { # have to force it to 0 or it will display the last value before it finished $SecondsRemaining = 0 } } # if the buffer if full, we need to resize it to make sure that the progress bar don't break if (($host.UI.RawUI.CursorPosition.y + 1) -ge ($host.UI.RawUI.BufferSize.Height)) { $size = New-Object System.Management.Automation.Host.Size(($host.UI.RawUI.BufferSize.Width), (($host.UI.RawUI.BufferSize.Height + 1000))) $host.UI.RawUI.BufferSize = $size } $cursorPosition = $host.UI.RawUI.CursorPosition #$cursorPositionY = $host.UI.RawUI.CursorPosition.Y [console]::CursorVisible=$false $windowWidth = [console]::WindowWidth # if screen is very small, don't display the percent if ($windowWidth -le 40) {$ShowPercent = $false} # calculate the size of the activity part of the output string if ($ActivityPadding -eq 0) { $activityPart = [math]::Floor($windowWidth / 4) } else { $activityPart = $ActivityPadding } # if activity string is longer than the allocated part length, truncate it if ($Activity.Length -gt $activityPart) { $Activity = Out-TruncatedString -String $Activity -Length $activityPart } $progressString = New-Object System.Text.StringBuilder -ArgumentList $windowWidth # add activity text to the progress string [void]$progressString.Append("$($Activity.PadRight($ActivityPart, ' ')) ") # add seconds elapsed to the progress string if ($PSBoundParameters.ContainsKey('SecondsElapsed')) { [void]$progressString.Append([timespan]::FromSeconds($SecondsElapsed).ToString() + ' ') } # add seconds remaining to the progress string if ($PSBoundParameters.ContainsKey('SecondsRemaining')) { [void]$progressString.Append([timespan]::FromSeconds($SecondsRemaining).ToString() + ' ') } # add the start bracket for the progress bar to the progress string [void]$progressString.Append($BarBracketStart) # calculate the width of the progress bar # the 5 is to account for the space of the percent information if ($ShowPercent) { $progressBarWidth = $windowWidth - (($progressString.Length) + 5) } else { $progressBarWidth = $windowWidth - ($progressString.Length) + 1 } # add one to the progress bar width if no end bracket is used if (-not ($BarBracketEnd)) { $progressBarWidth++ } # calculate the bar character percentage and how much of the bar is filled and how much is not filled $barCharacterInPercent = ($progressBarWidth - 2) / 100 $barProgressed = [math]::Floor($PercentComplete * $barCharacterInPercent) $barNotProgressed = ($progressBarWidth - 2) - $barProgressed # add the progress bar to progress string if ($barProgressed -gt 0) { if ($barNotProgressed -gt 0) { [void]$progressString.Append(($ProgressFillCharacter * ($barProgressed - 1))) [void]$progressString.Append($ProgressCharacter) } else { [void]$progressString.Append(($ProgressFillCharacter * $barProgressed)) } } [void]$progressString.Append("$($ProgressFill * $barNotProgressed)$($BarBracketEnd)") # add the percent complete to the progress string if ($ShowPercent) { [void]$progressString.Append(" $($PercentComplete.ToString().PadLeft(3, ' '))% ") } # if not already present, create a string builder to hold the last progress string if (-not ($script:lastProgressString)) { $script:lastProgressString = New-Object System.Text.StringBuilder($windowWidth) } # only update the progress string if it's different from the last (optimization) if (-not($script:lastProgressString.ToString() -eq $progressString.ToString())) { if ($UseWriteOutput) { Write-Output ($progressString.ToString()) } else { [console]::Write(($progressString.ToString())) } $host.UI.RawUI.CursorPosition = $cursorPosition #$host.UI.RawUI.CursorPosition = 0, $cursorPositionY [void]$script:lastProgressString.Clear() [void]$script:lastProgressString.Append($progressString.ToString()) } if ($Completed) { # do some clean-up and jump to the next line Remove-Variable -Name 'lastProgressString' -Scope 'Script' -ErrorAction SilentlyContinue [console]::CursorVisible = $true [console]::WriteLine() } } } else { Write-Warning 'This function is not compatible with PowerShell ISE.' } } function Get-GSOrganizationalUnitListPrivate { [cmdletbinding()] Param ( [parameter(Mandatory = $false,Position = 0)] [Alias('OrgUnitPath','BaseOrgUnitPath')] [String] $SearchBase, [parameter(Mandatory = $false)] [Alias('Type')] [ValidateSet('Subtree','OneLevel','All','Children')] [String] $SearchScope = 'All' ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.orgunit' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { try { Write-Verbose "Getting all Organizational Units" $request = $service.Orgunits.List($Script:PSGSuite.CustomerId) $request.Type = switch ($SearchScope) { Subtree { 'All' } OneLevel { 'Children' } default { $SearchScope } } if ($SearchBase) { $request.OrgUnitPath = $SearchBase } $request.Execute() | Select-Object -ExpandProperty OrganizationUnits } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } function Get-GSResourceListPrivate { [CmdletBinding()] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipelineByPropertyName = $true)] [ValidateSet('Calendars','Buildings','Features')] [String[]] $Resource = @('Calendars','Buildings','Features'), [parameter(Mandatory = $false)] [Alias('Query')] [String[]] $Filter, [parameter(Mandatory = $false)] [String[]] $OrderBy, [parameter(Mandatory = $false)] [ValidateRange(1,500)] [Alias("MaxResults")] [Int] $PageSize = "500" ) Begin { if ($MyInvocation.InvocationName -eq 'Get-GSCalendarResourceList') { $Resource = 'Calendars' } $propHash = @{ Calendars = 'Items' Buildings = 'BuildingsValue' Features = 'FeaturesValue' } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.resource.calendar' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { try { foreach ($R in $Resource) { $request = $service.Resources.$R.List($(if($Script:PSGSuite.CustomerID) {$Script:PSGSuite.CustomerID}else {'my_customer'})) if ($R -eq 'Calendars') { if ($PageSize) { $request.MaxResults = $PageSize } if ($OrderBy) { $request.OrderBy = "$($OrderBy -join ", ")" } if ($Filter) { if ($Filter -eq '*') { $Filter = "" } else { $Filter = "$($Filter -join " ")" } $Filter = $Filter -replace " -eq ","=" -replace " -like ",":" -replace " -match ",":" -replace " -contains ",":" -creplace "'True'","True" -creplace "'False'","False" $request.Query = $Filter.Trim() Write-Verbose "Getting Resource $R matching filter: `"$($Filter.Trim())`"" } else { Write-Verbose "Getting all Resource $R" } } else { Write-Verbose "Getting all Resource $R" } [int]$i = 1 do { $result = $request.Execute() $result.$($propHash[$R]) | ForEach-Object { $obj = $_ $_Id = switch ($R) { Calendars { $obj.ResourceId } Buildings { $obj.BuildingId } Features { $obj.Name } } $_ | Add-Member -MemberType NoteProperty -Name 'Id' -Value $_Id -PassThru | Add-Member -MemberType NoteProperty -Name 'Resource' -Value $R -PassThru | Add-Member -MemberType ScriptMethod -Name ToString -Value {$this.Id} -PassThru -Force } if ($result.NextPageToken) { $request.PageToken = $result.NextPageToken } [int]$retrieved = ($i + $result.$($propHash[$R]).Count) - 1 Write-Verbose "Retrieved $retrieved resources..." [int]$i = $i + $result.$($propHash[$R]).Count } until (!$result.NextPageToken) } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } function Get-GSShortUrlListPrivate { [cmdletbinding()] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail","Email")] [ValidateNotNullOrEmpty()] [String[]] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory=$false)] [ValidateSet("Full","Analytics_Clicks")] [string] $Projection = "Full" ) Process { foreach ($U in $User) { if ($U -ceq 'me') { $U = $Script:PSGSuite.AdminEmail } elseif ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/urlshortener' ServiceType = 'Google.Apis.Urlshortener.v1.UrlshortenerService' User = "$U" } $service = New-GoogleService @serviceParams try { Write-Verbose "Getting Short Url list for User '$U'" $request = $service.Url.List() $result = $request.Execute() if ($null -ne $result.Items) { $result.Items | Add-Member -MemberType NoteProperty -Name 'User' -Value $U -PassThru } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } function Get-GSUserASPListPrivate { [cmdletbinding()] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String[]] $User = $Script:PSGSuite.AdminEmail ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.user.security' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { foreach ($U in $User) { try { if ($U -ceq 'me') { $U = $Script:PSGSuite.AdminEmail } elseif ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } Write-Verbose "Getting ASP list for User '$U'" $request = $service.Asps.List($U) $result = $request.Execute() if ($null -ne $result.Items) { $result.Items | Add-Member -MemberType NoteProperty -Name 'User' -Value $U -PassThru } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } function Get-GSUserLicenseListPrivate { [cmdletbinding()] Param ( [parameter(Mandatory = $false)] [ValidateSet("Google-Apps","Google-Drive-storage","Google-Vault")] [string[]] $ProductID = @("Google-Apps","Google-Drive-storage","Google-Vault"), [parameter(Mandatory = $false)] [Alias("SkuId")] [ValidateSet("G-Suite-Enterprise","Google-Apps-Unlimited","Google-Apps-For-Business","Google-Apps-For-Postini","Google-Apps-Lite","Google-Drive-storage-20GB","Google-Drive-storage-50GB","Google-Drive-storage-200GB","Google-Drive-storage-400GB","Google-Drive-storage-1TB","Google-Drive-storage-2TB","Google-Drive-storage-4TB","Google-Drive-storage-8TB","Google-Drive-storage-16TB","Google-Vault","Google-Vault-Former-Employee","1010020020")] [string] $License, [parameter(Mandatory = $false)] [Alias("MaxResults")] [ValidateRange(1,1000)] [Int] $PageSize = "1000" ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/apps.licensing' ServiceType = 'Google.Apis.Licensing.v1.LicensingService' } $service = New-GoogleService @serviceParams if ($License) { $ProductID = @{ '1010020020' = 'Google-Apps' 'G-Suite-Enterprise' = 'Google-Apps' 'Google-Apps-Unlimited' = 'Google-Apps' 'Google-Apps-For-Business' = 'Google-Apps' 'Google-Apps-For-Postini' = 'Google-Apps' 'Google-Apps-Lite' = 'Google-Apps' 'Google-Vault' = 'Google-Vault' 'Google-Vault-Former-Employee' = 'Google-Vault' 'Google-Drive-storage-20GB' = 'Google-Drive-storage' 'Google-Drive-storage-50GB' = 'Google-Drive-storage' 'Google-Drive-storage-200GB' = 'Google-Drive-storage' 'Google-Drive-storage-400GB' = 'Google-Drive-storage' 'Google-Drive-storage-1TB' = 'Google-Drive-storage' 'Google-Drive-storage-2TB' = 'Google-Drive-storage' 'Google-Drive-storage-4TB' = 'Google-Drive-storage' 'Google-Drive-storage-8TB' = 'Google-Drive-storage' 'Google-Drive-storage-16TB' = 'Google-Drive-storage' }[$License] } $response = @() } Process { try { foreach ($prodId in $ProductID) { if ($License) { if ($License -eq "G-Suite-Enterprise") { $License = "1010020020" } $request = $service.LicenseAssignments.ListForProductAndSku($prodId,$License,$Script:PSGSuite.Domain) } else { $request = $service.LicenseAssignments.ListForProduct($prodId,$Script:PSGSuite.Domain) } if ($PageSize) { $request.MaxResults = $PageSize } [int]$i = 1 do { $result = $request.Execute() $response += $result.Items $request.PageToken = $result.NextPageToken [int]$retrieved = ($i + $result.Items.Count) - 1 if ($License) { Write-Verbose "Retrieved $retrieved licenses for product '$prodId' & sku '$License'..." } else { Write-Verbose "Retrieved $retrieved licenses for product '$prodId'..." } [int]$i = $i + $result.Items.Count } until (!$result.NextPageToken) } Write-Verbose "Retrieved $($response.Count) total licenses" return $response } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } function Get-GSUserSchemaListPrivate { [cmdletbinding()] Param( ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.userschema' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { try { $request = $service.Schemas.List($Script:PSGSuite.CustomerId) $result = $request.Execute() if ($null -ne $result.SchemasValue) { $result.SchemasValue } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } function Get-GSUserTokenListPrivate { [cmdletbinding()] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String[]] $User = $Script:PSGSuite.AdminEmail ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.user.security' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { foreach ($U in $User) { try { if ($U -ceq 'me') { $U = $Script:PSGSuite.AdminEmail } elseif ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } Write-Verbose "Getting Token list for User '$U'" $request = $service.Tokens.List($U) $result = $request.Execute() if ($null -ne $result.Items) { $result.Items | Add-Member -MemberType NoteProperty -Name 'User' -Value $U -PassThru } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } function Get-GSToken { <# .Synopsis Requests an Access Token for REST API authentication. Defaults to 3600 seconds token expiration time. .DESCRIPTION Requests an Access Token for REST API authentication. Defaults to 3600 seconds token expiration time. .EXAMPLE $Token = Get-GSToken -Scopes 'https://www.google.com/m8/feeds' -AdminEmail $User $headers = @{ Authorization = "Bearer $($Token)" 'GData-Version' = '3.0' } #> Param ( [parameter(Mandatory = $true)] [Alias('Scope')] [ValidateNotNullOrEmpty()] [string[]] $Scopes, [parameter(Mandatory = $false)] [Alias('User')] [ValidateNotNullOrEmpty()] [String] $AdminEmail = $Script:PSGSuite.AdminEmail ) try { Write-Verbose "Acquiring access token..." $serviceParams = @{ Scope = $Scopes ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $AdminEmail } $service = New-GoogleService @serviceParams ($service.HttpClientInitializer.GetAccessTokenForRequestAsync()).Result } catch { Write-Verbose "Failed to acquire access token!" $PSCmdlet.ThrowTerminatingError($_) } } Export-ModuleMember -Function 'Get-GSToken' function New-GoogleService { [CmdletBinding()] Param( [Parameter(Mandatory = $true,Position = 0)] [ValidateNotNullOrEmpty()] [String[]] $Scope, [Parameter(Mandatory = $true,Position = 1)] [String] $ServiceType, [Parameter(Mandatory = $false,Position = 2)] [Alias('AdminEmail')] [String] $User = $script:PSGSuite.AdminEmail ) Process { try { if ($script:PSGSuite.P12KeyPath) { Write-Verbose "Building ServiceAccountCredential from P12Key as user '$User'" $certificate = New-Object 'System.Security.Cryptography.X509Certificates.X509Certificate2' -ArgumentList (Resolve-Path $script:PSGSuite.P12KeyPath),"notasecret",([System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable) $credential = New-Object 'Google.Apis.Auth.OAuth2.ServiceAccountCredential' (New-Object 'Google.Apis.Auth.OAuth2.ServiceAccountCredential+Initializer' $script:PSGSuite.AppEmail -Property @{ User = $User Scopes = [string[]]$Scope } ).FromCertificate($certificate) } elseif ($script:PSGSuite.ClientSecretsPath -or $script:PSGSuite.ClientSecrets) { $ClientSecretsScopes = @( 'https://www.google.com/m8/feeds' 'https://mail.google.com' 'https://www.googleapis.com/auth/gmail.settings.basic' 'https://www.googleapis.com/auth/gmail.settings.sharing' 'https://www.googleapis.com/auth/calendar' 'https://www.googleapis.com/auth/drive' 'https://www.googleapis.com/auth/tasks' 'https://www.googleapis.com/auth/tasks.readonly' ) if (!$script:PSGSuite.ClientSecrets) { $script:PSGSuite.ClientSecrets = (Get-Content $script:PSGSuite.ClientSecretsPath -Raw) Set-PSGSuiteConfig -ConfigName $script:PSGSuite.ConfigName -ClientSecretsPath $script:PSGSuite.ClientSecretsPath -Verbose:$false } Write-Verbose "Building UserCredentials from ClientSecrets as user '$User'" $stream = New-Object System.IO.MemoryStream $([System.Text.Encoding]::ASCII.GetBytes(($script:PSGSuite.ClientSecrets))),$null $credPath = Join-Path (Resolve-Path (Join-Path "~" ".scrthq")) "PSGSuite" $credential = [Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker]::AuthorizeAsync( [Google.Apis.Auth.OAuth2.GoogleClientSecrets]::Load($stream).Secrets, [string[]]$ClientSecretsScopes, $User, [System.Threading.CancellationToken]::None, [Google.Apis.Util.Store.FileDataStore]::new($credPath,$true) ).Result $stream.Close() } else { $PSCmdlet.ThrowTerminatingError((ThrowTerm "The current config '$($script:PSGSuite.ConfigName)' does not contain a P12KeyPath or a ClientSecretsPath! PSGSuite is unable to build a credential object for the service without a path to a credential file! Please update the configuration to include a path at least one of the two credential types.")) } New-Object "$ServiceType" (New-Object 'Google.Apis.Services.BaseClientService+Initializer' -Property @{ HttpClientInitializer = $credential ApplicationName = "PSGSuite - $env:USERNAME" } ) } catch { $PSCmdlet.ThrowTerminatingError($_) } } } Export-ModuleMember -Function 'New-GoogleService' function Add-GSCalendarSubscription { <# .SYNOPSIS Adds a calendar to a users calendar list (aka subscribes to the specified calendar) .DESCRIPTION Adds a calendar to a users calendar list (aka subscribes to the specified calendar) .PARAMETER User The primary email or UserID of the user. You can exclude the '@domain.com' to insert the Domain in the config or use the special 'me' to indicate the AdminEmail in the config. .PARAMETER CalendarID The calendar ID of the calendar you would like to subscribe the user to .PARAMETER Selected Whether the calendar content shows up in the calendar UI. Optional. The default is False. .PARAMETER Hidden Whether the calendar has been hidden from the list. Optional. The default is False. .PARAMETER DefaultReminderMethod The method used by this reminder. Defaults to email. Possible values are: * "email" - Reminders are sent via email. * "sms" - Reminders are sent via SMS. These are only available for G Suite customers. Requests to set SMS reminders for other account types are ignored. * "popup" - Reminders are sent via a UI popup. .PARAMETER DefaultReminderMinutes Number of minutes before the start of the event when the reminder should trigger. Defaults to 30 minutes. Valid values are between 0 and 40320 (4 weeks in minutes). .PARAMETER DefaultNotificationMethod The method used to deliver the notification. Defaults to email. Possible values are: * "email" - Reminders are sent via email. * "sms" - Reminders are sent via SMS. This value is read-only and is ignored on inserts and updates. SMS reminders are only available for G Suite customers. .PARAMETER DefaultNotificationType The type of notification. Defaults to eventChange. Possible values are: * "eventCreation" - Notification sent when a new event is put on the calendar. * "eventChange" - Notification sent when an event is changed. * "eventCancellation" - Notification sent when an event is cancelled. * "eventResponse" - Notification sent when an event is changed. * "agenda" - An agenda with the events of the day (sent out in the morning). .PARAMETER Color The color of the calendar. .PARAMETER SummaryOverride The summary that the authenticated user has set for this calendar. .EXAMPLE Add-GSCalendarSubscription -User me -CalendarId john.smith@domain.com -Selected -Color Cyan Adds the calendar 'john.smith@domain.com' to the AdminEmail user's calendar list #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String] $User, [parameter(Mandatory = $true)] [String[]] $CalendarId, [parameter(Mandatory = $false)] [Switch] $Selected, [parameter(Mandatory = $false)] [Switch] $Hidden, [parameter(Mandatory = $false)] [ValidateSet('email','sms','popup')] [String] $DefaultReminderMethod = 'email', [parameter(Mandatory = $false)] [ValidateRange(0,40320)] [Int] $DefaultReminderMinutes = 30, [parameter(Mandatory = $false)] [ValidateSet('email','sms')] [String] $DefaultNotificationMethod = 'email', [parameter(Mandatory = $false)] [ValidateSet('eventCreation','eventChange','eventCancellation','eventResponse','agenda')] [String] $DefaultNotificationType = 'eventChange', [parameter(Mandatory = $false)] [ValidateSet("Periwinkle","Seafoam","Lavender","Coral","Goldenrod","Beige","Cyan","Grey","Blue","Green","Red")] [String] $Color, [parameter(Mandatory = $false)] [String] $SummaryOverride ) Begin { $colorHash = @{ Periwinkle = 1 Seafoam = 2 Lavender = 3 Coral = 4 Goldenrod = 5 Beige = 6 Cyan = 7 Grey = 8 Blue = 9 Green = 10 Red = 11 } } Process { try { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/calendar' ServiceType = 'Google.Apis.Calendar.v3.CalendarService' User = $User } $service = New-GoogleService @serviceParams foreach ($calId in $CalendarID) { $body = New-Object 'Google.Apis.Calendar.v3.Data.CalendarListEntry' -Property @{ Id = $calId Selected = $Selected Hidden = $Hidden } $DefaultReminders = New-Object 'Google.Apis.Calendar.v3.Data.EventReminder' -Property @{ Method = $DefaultReminderMethod Minutes = $DefaultReminderMinutes } $body.DefaultReminders = [Google.Apis.Calendar.v3.Data.EventReminder[]]$DefaultReminders $DefaultNotification = New-Object 'Google.Apis.Calendar.v3.Data.CalendarNotification' $DefaultNotification.Method = $DefaultNotificationMethod $DefaultNotification.Type = $DefaultNotificationType $body.NotificationSettings = New-Object 'Google.Apis.Calendar.v3.Data.CalendarListEntry+NotificationSettingsData' -Property @{ Notifications = [Google.Apis.Calendar.v3.Data.CalendarNotification[]]$DefaultNotification } foreach ($key in $PSBoundParameters.Keys) { switch ($key) { Color { $body.ColorId = $colorHash[$Color] } Default { if ($body.PSObject.Properties.Name -contains $key) { $body.$key = $PSBoundParameters[$key] } } } } Write-Verbose "Subscribing user '$User' to Calendar '$($calId)'" $request = $service.CalendarList.Insert($body) $request.Execute() } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Add-GSCalendarSubscription' function Get-GSCalendarACL { <# .SYNOPSIS Gets the ACL calendar for a calendar .DESCRIPTION Gets the ACL for a calendar .PARAMETER User The primary email or UserID of the user. You can exclude the '@domain.com' to insert the Domain in the config or use the special 'me' to indicate the AdminEmail in the config. Defaults to the AdminEmail in the config .PARAMETER CalendarId The calendar ID of the calendar you would like to list ACLS for. Defaults to the user's primary calendar .PARAMETER RuleId The Id of the Rule you would like to retrieve specifically. Leave empty to return the full ACL list instead. .PARAMETER PageSize Maximum number of events returned on one result page. .EXAMPLE Get-GSCalendarACL -User me -CalendarID "primary" This gets the ACL on the primary calendar of the AdminUser. #> [cmdletbinding(DefaultParameterSetName = 'List')] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String[]] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false)] [String] $CalendarId = "primary", [parameter(Mandatory = $false, ParameterSetName = 'Get')] [String] $RuleId, [parameter(Mandatory = $false, ParameterSetName = 'List')] [ValidateRange(1,2500)] [Int] $PageSize = 2500 ) Process { foreach ($U in $User) { if ($U -ceq 'me') { $U = $Script:PSGSuite.AdminEmail } elseif ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/calendar' ServiceType = 'Google.Apis.Calendar.v3.CalendarService' User = $U } $service = New-GoogleService @serviceParams try { switch ($PSCmdlet.ParameterSetName) { Get { Write-Verbose "Getting ACL Id '$RuleId' of calendar '$CalendarId' for user '$U'" $request = $service.Acl.Get($CalendarId,$RuleId) $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $U -PassThru | Add-Member -MemberType NoteProperty -Name 'CalendarId' -Value $CalendarId -PassThru } List { $request = $service.Acl.List($CalendarId) foreach ($key in $PSBoundParameters.Keys | Where-Object {$_ -ne 'CalendarId'}) { switch ($key) { Default { if ($request.PSObject.Properties.Name -contains $key) { $request.$key = $PSBoundParameters[$key] } } } } if ($PageSize) { $request.MaxResults = $PageSize } Write-Verbose "Getting ACL List of calendar '$CalendarId' for user '$U'" [int]$i = 1 do { $result = $request.Execute() $result.Items | Add-Member -MemberType NoteProperty -Name 'User' -Value $U -PassThru | Add-Member -MemberType NoteProperty -Name 'CalendarId' -Value $CalendarId -PassThru if ($result.NextPageToken) { $request.PageToken = $result.NextPageToken } [int]$retrieved = ($i + $result.Items.Count) - 1 Write-Verbose "Retrieved $retrieved Calendar Events..." [int]$i = $i + $result.Items.Count } until (!$result.NextPageToken) } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Get-GSCalendarACL' function Get-GSCalendarEvent { <# .SYNOPSIS Gets the calendar events for a user .DESCRIPTION Gets the calendar events for a user .PARAMETER User The primary email or UserID of the user. You can exclude the '@domain.com' to insert the Domain in the config or use the special 'me' to indicate the AdminEmail in the config. Defaults to the AdminEmail in the config .PARAMETER CalendarId The calendar ID of the calendar you would like to list events from. Defaults to the user's primary calendar .PARAMETER Filter Free text search terms to find events that match these terms in any field, except for extended properties. .PARAMETER OrderBy The order of the events returned in the result. Acceptable values are: * "startTime": Order by the start date/time (ascending). This is only available when querying single events (i.e. the parameter singleEvents is True) * "updated": Order by last modification time (ascending). .PARAMETER MaxAttendees The maximum number of attendees to include in the response. If there are more than the specified number of attendees, only the participant is returned. .PARAMETER PageSize Maximum number of events returned on one result page. .PARAMETER ShowDeleted Whether to include deleted events (with status equals "cancelled") in the result. Cancelled instances of recurring events (but not the underlying recurring event) will still be included if showDeleted and singleEvents are both False. If showDeleted and singleEvents are both True, only single instances of deleted events (but not the underlying recurring events) are returned. .PARAMETER ShowHiddenInvitations Whether to include hidden invitations in the result. .PARAMETER SingleEvents Whether to expand recurring events into instances and only return single one-off events and instances of recurring events, but not the underlying recurring events themselves. .PARAMETER TimeMin Lower bound (inclusive) for an event's end time to filter by. If TimeMax is set, TimeMin must be smaller than timeMax. .PARAMETER TimeMax Upper bound (exclusive) for an event's start time to filter by. If TimeMin is set, TimeMax must be greater than timeMin. .EXAMPLE Get-GSCalendarEventList -TimeMin (Get-Date "01-21-2018 00:00:00") -TimeMax (Get-Date "01-28-2018 23:59:59") -SingleEvents This gets the single events on the primary calendar of the Admin for the week of Jan 21-28, 2018. #> [cmdletbinding(DefaultParameterSetName = "List")] Param ( [parameter(Mandatory = $true,Position = 0,ParameterSetName = "Get")] [String[]] $EventId, [parameter(Mandatory = $false)] [String] $CalendarId = "primary", [parameter(Mandatory = $false,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String[]] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false,ParameterSetName = "List")] [Alias('Q','Query')] [String] $Filter, [parameter(Mandatory = $false,ParameterSetName = "List")] [ValidateSet("StartTime","Updated")] [String] $OrderBy, [parameter(Mandatory = $false)] [Int] $MaxAttendees, [parameter(Mandatory = $false,ParameterSetName = "List")] [ValidateRange(1,2500)] [Int] $PageSize = 2500, [parameter(Mandatory = $false,ParameterSetName = "List")] [switch] $ShowDeleted, [parameter(Mandatory = $false,ParameterSetName = "List")] [switch] $ShowHiddenInvitations, [parameter(Mandatory = $false,ParameterSetName = "List")] [switch] $SingleEvents, [parameter(Mandatory = $false,ParameterSetName = "List")] [Hashtable] $PrivateExtendedProperty, [parameter(Mandatory = $false,ParameterSetName = "List")] [Hashtable] $SharedExtendedProperty, [parameter(Mandatory = $false,ParameterSetName = "List")] [DateTime] $TimeMin, [parameter(Mandatory = $false,ParameterSetName = "List")] [DateTime] $TimeMax ) Process { foreach ($U in $User) { if ($U -ceq 'me') { $U = $Script:PSGSuite.AdminEmail } elseif ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/calendar' ServiceType = 'Google.Apis.Calendar.v3.CalendarService' User = $U } $service = New-GoogleService @serviceParams foreach ($calId in $CalendarId) { switch ($PSCmdlet.ParameterSetName) { Get { foreach ($evId in $EventId) { try { $request = $service.Events.Get($calId,$evId) if ($PSBoundParameters.Keys -contains 'MaxAttendees') { $request.MaxAttendees = $MaxAttendees } Write-Verbose "Getting Event ID '$evId' from Calendar '$calId' for User '$U'" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $U -PassThru | Add-Member -MemberType NoteProperty -Name 'CalendarId' -Value $calId -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } List { try { $request = $service.Events.List($calId) foreach ($key in $PSBoundParameters.Keys | Where-Object {$_ -ne 'CalendarId'}) { switch ($key) { Filter { $request.Q = $Filter } PrivateExtendedProperty { $converted = $PrivateExtendedProperty.Keys | Foreach-Object { "$($_)=$($PrivateExtendedProperty[$_])" } $repeatable = [Google.Apis.Util.Repeatable[String]]::new([String[]]$converted) $request.PrivateExtendedProperty = $repeatable } SharedExtendedProperty { $converted = $SharedExtendedProperty.Keys | Foreach-Object { "$($_)=$($SharedExtendedProperty[$_])" } $repeatable = [Google.Apis.Util.Repeatable[String]]::new([String[]]$converted) $request.SharedExtendedProperty = $repeatable } Default { if ($request.PSObject.Properties.Name -contains $key) { $request.$key = $PSBoundParameters[$key] } } } } if ($PageSize) { $request.MaxResults = $PageSize } if ($Filter) { Write-Verbose "Getting all Calendar Events matching filter '$Filter' on calendar '$calId' for user '$U'" } else { Write-Verbose "Getting all Calendar Events on calendar '$calId' for user '$U'" } [int]$i = 1 do { $result = $request.Execute() $result.Items | Add-Member -MemberType NoteProperty -Name 'User' -Value $U -PassThru | Add-Member -MemberType NoteProperty -Name 'CalendarId' -Value $calId -PassThru if ($result.NextPageToken) { $request.PageToken = $result.NextPageToken } [int]$retrieved = ($i + $result.Items.Count) - 1 Write-Verbose "Retrieved $retrieved Calendar Events..." [int]$i = $i + $result.Items.Count } until (!$result.NextPageToken) } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } } } } Export-ModuleMember -Function 'Get-GSCalendarEvent' function Get-GSCalendarSubscription { <# .SYNOPSIS Gets a subscribed calendar from a users calendar list. Returns the full calendar list if no CalendarId is specified. .DESCRIPTION Gets a subscribed calendar from a users calendar list. Returns the full calendar list if no CalendarId is specified. .PARAMETER User The primary email or UserID of the user. You can exclude the '@domain.com' to insert the Domain in the config or use the special 'me' to indicate the AdminEmail in the config. Defaults to the AdminEmail in the config .PARAMETER CalendarID The calendar ID of the calendar you would like to get info for. If left blank, returns the list of calendars the user is subscribed to. .EXAMPLE Get-GSCalendarSubscription Gets the AdminEmail user's calendar list #> [cmdletbinding()] Param ( [parameter(Mandatory = $false,Position = 0)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false,Position = 1)] [String[]] $CalendarId ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/calendar' ServiceType = 'Google.Apis.Calendar.v3.CalendarService' User = $User } $service = New-GoogleService @serviceParams } Process { if ($PSBoundParameters.Keys -contains 'CalendarId') { foreach ($calId in $CalendarID) { try { Write-Verbose "Getting subscribed calendar '$($calId)' for user '$User'" $request = $service.CalendarList.Get($calId) $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } else { try { Write-Verbose "Getting subscribed calendar list for user '$User'" $request = $service.CalendarList.List() $request.Execute() | Select-Object -ExpandProperty Items } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Get-GSCalendarSubscription' function New-GSCalendarACL { <# .SYNOPSIS Adds Google User to Calendar .DESCRIPTION Adds Google User to Calendar .PARAMETER User The primary email or UserID of the user. You can exclude the '@domain.com' to insert the Domain in the config or use the special 'me' to indicate the AdminEmail in the config. Defaults to the AdminEmail in the config. .PARAMETER CalendarID The Id of the calendar you would like to share Defaults to the user's primary calendar. .PARAMETER Role The role assigned to the scope. Available values are: * "none" - Provides no access. * "freeBusyReader" - Provides read access to free/busy information. * "reader" - Provides read access to the calendar. Private events will appear to users with reader access, but event details will be hidden. * "writer" - Provides read and write access to the calendar. Private events will appear to users with writer access, and event details will be visible. * "owner" - Provides ownership of the calendar. This role has all of the permissions of the writer role with the additional ability to see and manipulate ACLs. .PARAMETER Value The email address of a user or group, or the name of a domain, depending on the scope type. Omitted for type "default". .PARAMETER Type The type of the scope. Available values are: * "default" - The public scope. This is the default value. * "user" - Limits the scope to a single user. * "group" - Limits the scope to a group. * "domain" - Limits the scope to a domain. Note: The permissions granted to the "default", or public, scope apply to any user, authenticated or not. .EXAMPLE New-GSCalendarACL -CalendarID jennyappleseed@domain.com -Role reader -Value Jonnyappleseed@domain.com -Type user Gives Jonnyappleseed@domain.com reader access to jennyappleseed's calendar. #> [cmdletbinding(DefaultParameterSetName = "AttendeeEmails")] Param ( [parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail", "UserKey", "Mail")] [ValidateNotNullOrEmpty()] [String[]] $User = "me", [parameter(Mandatory = $false)] [String[]] $CalendarId = "primary", [parameter(Mandatory = $true)] [ValidateSet("owner", "writer", "reader", "none", "freeBusyReader")] [String] $Role, [parameter(Mandatory = $true)] [String] $Value, [parameter(Mandatory = $false)] [ValidateSet("default", "user", "group", "domain")] [String] $Type = "user" ) Process { foreach ($U in $User) { if ($U -ceq 'me') { $U = $Script:PSGSuite.AdminEmail } elseif ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/calendar' ServiceType = 'Google.Apis.Calendar.v3.CalendarService' User = $U } $service = New-GoogleService @serviceParams foreach ($calId in $CalendarID) { try { $body = New-Object 'Google.Apis.Calendar.v3.Data.AclRule' $scopeData = New-Object "Google.Apis.Calendar.v3.Data.AclRule+ScopeData" foreach ($key in $PSBoundParameters.Keys) { switch ($key) { Role { $body.Role = $PSBoundParameters[$key] } Type { $scopeData.Type = $PSBoundParameters[$key] } Value { $scopeData.Value = $PSBoundParameters[$key] } Default { if ($body.PSObject.Properties.Name -contains $key) { $body.$key = $PSBoundParameters[$key] } } } } Write-Verbose "Inserting new ACL for type '$Type' with value '$Value' in role '$Role' on calendar '$calId' for user '$U'" $body.Scope = $scopeData $request = $service.Acl.Insert($body, $calId) $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } } Export-ModuleMember -Function 'New-GSCalendarACL' function New-GSCalendarEvent { <# .SYNOPSIS Creates a new calendar event .DESCRIPTION Creates a new calendar event .PARAMETER Summary Event summary .PARAMETER Description Event description .PARAMETER Id Opaque identifier of the event. When creating new single or recurring events, you can specify their IDs. Provided IDs must follow these rules: * characters allowed in the ID are those used in base32hex encoding, i.e. lowercase letters a-v and digits 0-9, see section 3.1.2 in RFC2938 * the length of the ID must be between 5 and 1024 characters * the ID must be unique per calendar Due to the globally distributed nature of the system, we cannot guarantee that ID collisions will be detected at event creation time. To minimize the risk of collisions we recommend using an established UUID algorithm such as one described in RFC4122. If you do not specify an ID, it will be automatically generated by the server. .PARAMETER User The primary email or UserID of the user. You can exclude the '@domain.com' to insert the Domain in the config or use the special 'me' to indicate the AdminEmail in the config. Defaults to the AdminEmail in the config. .PARAMETER CalendarID The calendar ID of the calendar you would like to list events from. Defaults to the user's primary calendar. .PARAMETER AttendeeEmails The email addresses of the attendees to add. NOTE: This performs simple adds without additional attendee options. If additional options are needed, use the Attendees parameter instead. .PARAMETER Attendees The EventAttendee object(s) to add. Use Add-GSEventAttendee with this parameter for best results. .PARAMETER Location Event location .PARAMETER EventColor Color of the event as seen in Calendar .PARAMETER DisableReminder When $true, disables inheritance of the default Reminders from the Calendar the event was created on. .PARAMETER LocalStartDateTime Start date and time of the event. Lowest precendence of the three StartDate parameters. Defaults to the time the function is ran. .PARAMETER LocalEndDateTime End date and time of the event. Lowest precendence of the three EndDate parameters. Defaults to 30 minutes after the time the function is ran. .PARAMETER StartDate String representation of the start date. Middle precendence of the three StartDate parameters. .PARAMETER EndDate String representation of the end date. Middle precendence of the three EndDate parameters. .PARAMETER UTCStartDateTime String representation of the start date in UTC. Highest precendence of the three StartDate parameters. .PARAMETER UTCEndDateTime String representation of the end date in UTC. Highest precendence of the three EndDate parameters. .PARAMETER PrivateExtendedProperties A hashtable of properties that are private to the copy of the event that appears on this calendar. .PARAMETER SharedExtendedProperties A hashtable of properties that are shared between copies of the event on other attendees' calendars. .PARAMETER ExtendedProperties Extended properties of the event. This must be of the type 'Google.Apis.Calendar.v3.Data.Event+ExtendedPropertiesData'. This is useful for copying another events ExtendedProperties over when creating a new event. .EXAMPLE New-GSCalendarEvent "Go to the gym" -StartDate (Get-Date "21:00:00") -EndDate (Get-Date "22:00:00") Creates an event titled "Go to the gym" for 9-10PM the day the function is ran. #> [cmdletbinding(DefaultParameterSetName = "AttendeeEmails")] Param ( [parameter(Mandatory = $true,Position = 0)] [String] $Summary, [parameter(Mandatory = $false)] [String] $Description, [parameter(Mandatory = $false)] [ValidateScript({if ($_ -match '^[0-9a-v]+$'){$true}else{throw "The characters allowed in the ID are only those used in base32hex encoding, i.e. lowercase letters a-v and digits 0-9"}})] [ValidateLength(5,1024)] [String] $Id, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String[]] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [String[]] $CalendarID = "primary", [parameter(Mandatory = $false,ParameterSetName = "AttendeeEmails")] [String[]] $AttendeeEmails, [parameter(Mandatory = $false,ParameterSetName = "AttendeeObjects")] [Google.Apis.Calendar.v3.Data.EventAttendee[]] $Attendees, [parameter(Mandatory = $false)] [String] $Location, [parameter(Mandatory = $false)] [ValidateSet("Periwinkle","Seafoam","Lavender","Coral","Goldenrod","Beige","Cyan","Grey","Blue","Green","Red")] [String] $EventColor, [parameter(Mandatory = $false)] [Switch] $DisableReminder, [parameter(Mandatory = $false)] [DateTime] $LocalStartDateTime = (Get-Date), [parameter(Mandatory = $false)] [DateTime] $LocalEndDateTime = (Get-Date).AddMinutes(30), [parameter(Mandatory = $false)] [String] $StartDate, [parameter(Mandatory = $false)] [String] $EndDate, [parameter(Mandatory = $false)] [String] $UTCStartDateTime, [parameter(Mandatory = $false)] [String] $UTCEndDateTime, [parameter(Mandatory = $false)] [Hashtable] $PrivateExtendedProperties, [parameter(Mandatory = $false)] [Hashtable] $SharedExtendedProperties, [parameter(Mandatory = $false)] [Google.Apis.Calendar.v3.Data.Event+ExtendedPropertiesData] $ExtendedProperties ) Begin { $colorHash = @{ Periwinkle = 1 Seafoam = 2 Lavender = 3 Coral = 4 Goldenrod = 5 Beige = 6 Cyan = 7 Grey = 8 Blue = 9 Green = 10 Red = 11 } } Process { try { foreach ($U in $User) { if ($U -ceq 'me') { $U = $Script:PSGSuite.AdminEmail } elseif ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/calendar' ServiceType = 'Google.Apis.Calendar.v3.CalendarService' User = $U } $service = New-GoogleService @serviceParams if ($PSCmdlet.ParameterSetName -eq 'AttendeeEmails' -and $PSBoundParameters.Keys -contains 'AttendeeEmails') { [Google.Apis.Calendar.v3.Data.EventAttendee[]]$Attendees = $AttendeeEmails | ForEach-Object { Add-GSEventAttendee -Email $_ } } $body = New-Object 'Google.Apis.Calendar.v3.Data.Event' if ($Attendees) { $body.Attendees = [Google.Apis.Calendar.v3.Data.EventAttendee[]]$Attendees } foreach ($key in $PSBoundParameters.Keys) { switch ($key) { EventColor { $body.ColorId = $colorHash[$EventColor] } PrivateExtendedProperties { if (-not $ExtendedProperties) { $ExtendedProperties = New-Object 'Google.Apis.Calendar.v3.Data.Event+ExtendedPropertiesData' -Property @{ Private__ = (New-Object 'System.Collections.Generic.Dictionary[string,string]') Shared = (New-Object 'System.Collections.Generic.Dictionary[string,string]') } } elseif (-not $ExtendedProperties.Private__) { $ExtendedProperties.Private__ = (New-Object 'System.Collections.Generic.Dictionary[string,string]') } foreach ($prop in $PrivateExtendedProperties.Keys) { $ExtendedProperties.Private__.Add($prop,$PrivateExtendedProperties[$prop]) } } SharedExtendedProperties { if (-not $ExtendedProperties) { $ExtendedProperties = New-Object 'Google.Apis.Calendar.v3.Data.Event+ExtendedPropertiesData' -Property @{ Private__ = (New-Object 'System.Collections.Generic.Dictionary[string,string]') Shared = (New-Object 'System.Collections.Generic.Dictionary[string,string]') } } elseif (-not $ExtendedProperties.Shared) { $ExtendedProperties.Shared = (New-Object 'System.Collections.Generic.Dictionary[string,string]') } foreach ($prop in $SharedExtendedProperties.Keys) { $ExtendedProperties.Shared.Add($prop,$SharedExtendedProperties[$prop]) } } DisableReminder { $reminder = New-Object 'Google.Apis.Calendar.v3.Data.Event+RemindersData' -Property @{ UseDefault = (-not $DisableReminder) } $body.Reminders = $reminder } Default { if ($body.PSObject.Properties.Name -contains $key) { $body.$key = $PSBoundParameters[$key] } } } } if ($ExtendedProperties) { $body.ExtendedProperties = $ExtendedProperties } $body.Start = if ($UTCStartDateTime) { New-Object 'Google.Apis.Calendar.v3.Data.EventDateTime' -Property @{ DateTime = $UTCStartDateTime } } elseif ($StartDate) { New-Object 'Google.Apis.Calendar.v3.Data.EventDateTime' -Property @{ Date = (Get-Date $StartDate -Format "yyyy-MM-dd") } } else { New-Object 'Google.Apis.Calendar.v3.Data.EventDateTime' -Property @{ DateTime = $LocalStartDateTime } } $body.End = if ($UTCEndDateTime) { New-Object 'Google.Apis.Calendar.v3.Data.EventDateTime' -Property @{ DateTime = $UTCEndDateTime } } elseif ($EndDate) { New-Object 'Google.Apis.Calendar.v3.Data.EventDateTime' -Property @{ Date = (Get-Date $EndDate -Format "yyyy-MM-dd") } } else { New-Object 'Google.Apis.Calendar.v3.Data.EventDateTime' -Property @{ DateTime = $LocalEndDateTime } } foreach ($calId in $CalendarID) { Write-Verbose "Creating Calendar Event '$($Summary)' on calendar '$calId' for user '$U'" $request = $service.Events.Insert($body,$calId) $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $U -PassThru | Add-Member -MemberType NoteProperty -Name 'CalendarId' -Value $calId -PassThru } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'New-GSCalendarEvent' function Remove-GSCalendarEvent { <# .SYNOPSIS Removes a calendar event .DESCRIPTION Removes a calendar event .PARAMETER User The primary email or UserID of the user. You can exclude the '@domain.com' to insert the Domain in the config or use the special 'me' to indicate the AdminEmail in the config. Defaults to the AdminEmail in the config. .PARAMETER CalendarID The calendar ID of the calendar you would like to list events from. Defaults to the user's primary calendar. .PARAMETER EventID The EventID to remove .EXAMPLE Remove-GSCalendarEvent -User user@domain.com -EventID _60q30c1g60o30e1i60o4ac1g60rj8gpl88rj2c1h84s34h9g60s30c1g60o30c1g84o3eg9n8gq32d246gq48d1g64o30c1g60o30c1g60o30c1g60o32c1g60o30c1g8csjihhi6oq3igi28h248ghk6ks4agq161144ga46gr4aci488p0 Removes the specified event from user@domain.com's calendar. #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High")] Param ( [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [String] $CalendarID = "primary", [parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)] [Alias('Id')] [String[]] $EventID ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/calendar' ServiceType = 'Google.Apis.Calendar.v3.CalendarService' User = $User } $service = New-GoogleService @serviceParams } Process { foreach ($E in $EventId) { try { if ($PSCmdlet.ShouldProcess("Deleting Event Id '$E' from user '$User'")) { Write-Verbose "Deleting Event Id '$E' from user '$User'" $request = $service.Events.Delete($CalendarID, $E) $request.Execute() Write-Verbose "Label Id '$E' deleted successfully from user '$User'" } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Remove-GSCalendarEvent' function Remove-GSCalendarSubscription { <# .SYNOPSIS Removes a calendar from a users calendar list (aka unsubscribes from the specified calendar) .DESCRIPTION Removes a calendar from a users calendar list (aka unsubscribes from the specified calendar) .PARAMETER User The primary email or UserID of the user. You can exclude the '@domain.com' to insert the Domain in the config or use the special 'me' to indicate the AdminEmail in the config. .PARAMETER CalendarID The calendar ID of the calendar you would like to unsubscribe the user from .EXAMPLE Remove-GSCalendarSubscription -User me -CalendarId john.smith@domain.com Removes the calendar 'john.smith@domain.com' from the AdminEmail user's calendar list #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $true,Position = 0)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String] $User, [parameter(Mandatory = $true,Position = 1)] [String[]] $CalendarId ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/calendar' ServiceType = 'Google.Apis.Calendar.v3.CalendarService' User = $User } $service = New-GoogleService @serviceParams } Process { foreach ($calId in $CalendarID) { try { if ($PSCmdlet.ShouldProcess("Unsubscribing user '$User' from Calendar '$($calId)'")) { Write-Verbose "Unsubscribing user '$User' from Calendar '$($calId)'" $request = $service.CalendarList.Delete($calId) $request.Execute() Write-Verbose "User '$User' has been successfully unsubscribed from calendar '$calId'" } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Remove-GSCalendarSubscription' function Update-GSCalendarEvent { <# .SYNOPSIS Updates an event .DESCRIPTION Updates an event .PARAMETER EventID The unique Id of the event to update .PARAMETER CalendarID The Id of the calendar Defaults to the user's primary calendar. .PARAMETER User The primary email or UserID of the user. You can exclude the '@domain.com' to insert the Domain in the config or use the special 'me' to indicate the AdminEmail in the config. Defaults to the AdminEmail in the config. .PARAMETER Summary Event summary .PARAMETER Description Event description .PARAMETER AttendeeEmails The email addresses of the attendees to add. NOTE: This performs simple adds without additional attendee options. If additional options are needed, use the Attendees parameter instead. .PARAMETER Attendees The EventAttendee object(s) to add. Use Add-GSEventAttendee with this parameter for best results. .PARAMETER Location Event location .PARAMETER EventColor Color of the event as seen in Calendar .PARAMETER DisableReminder When $true, disables inheritance of the default Reminders from the Calendar the event was created on. .PARAMETER LocalStartDateTime Start date and time of the event. Lowest precendence of the three StartDate parameters. Defaults to the time the function is ran. .PARAMETER LocalEndDateTime End date and time of the event. Lowest precendence of the three EndDate parameters. Defaults to 30 minutes after the time the function is ran. .PARAMETER StartDate String representation of the start date. Middle precendence of the three StartDate parameters. .PARAMETER EndDate String representation of the end date. Middle precendence of the three EndDate parameters. .PARAMETER UTCStartDateTime String representation of the start date in UTC. Highest precendence of the three StartDate parameters. .PARAMETER UTCEndDateTime String representation of the end date in UTC. Highest precendence of the three EndDate parameters. .PARAMETER PrivateExtendedProperties A hashtable of properties that are private to the copy of the event that appears on this calendar. .PARAMETER SharedExtendedProperties A hashtable of properties that are shared between copies of the event on other attendees' calendars. .PARAMETER ExtendedProperties Extended properties of the event. This must be of the type 'Google.Apis.Calendar.v3.Data.Event+ExtendedPropertiesData'. This is useful for copying another events ExtendedProperties over when updating an existing event. .EXAMPLE New-GSCalendarEvent "Go to the gym" -StartDate (Get-Date "21:00:00") -EndDate (Get-Date "22:00:00") Creates an event titled "Go to the gym" for 9-10PM the day the function is ran. #> [cmdletbinding(DefaultParameterSetName = "AttendeeEmails")] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias('Id')] [String[]] $EventId, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [String[]] $CalendarId = "primary", [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String[]] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false)] [String] $Summary, [parameter(Mandatory = $false)] [String] $Description, [parameter(Mandatory = $false,ParameterSetName = "AttendeeEmails")] [String[]] $AttendeeEmails, [parameter(Mandatory = $false,ParameterSetName = "AttendeeObjects")] [Google.Apis.Calendar.v3.Data.EventAttendee[]] $Attendees, [parameter(Mandatory = $false)] [String] $Location, [parameter(Mandatory = $false)] [ValidateSet("Periwinkle","Seafoam","Lavender","Coral","Goldenrod","Beige","Cyan","Grey","Blue","Green","Red")] [String] $EventColor, [parameter(Mandatory = $false)] [Switch] $DisableReminder, [parameter(Mandatory = $false)] [DateTime] $LocalStartDateTime, [parameter(Mandatory = $false)] [DateTime] $LocalEndDateTime, [parameter(Mandatory = $false)] [String] $StartDate, [parameter(Mandatory = $false)] [String] $EndDate, [parameter(Mandatory = $false)] [String] $UTCStartDateTime, [parameter(Mandatory = $false)] [String] $UTCEndDateTime, [parameter(Mandatory = $false)] [Hashtable] $PrivateExtendedProperties, [parameter(Mandatory = $false)] [Hashtable] $SharedExtendedProperties, [parameter(Mandatory = $false)] [Google.Apis.Calendar.v3.Data.Event+ExtendedPropertiesData] $ExtendedProperties ) Begin { $colorHash = @{ Periwinkle = 1 Seafoam = 2 Lavender = 3 Coral = 4 Goldenrod = 5 Beige = 6 Cyan = 7 Grey = 8 Blue = 9 Green = 10 Red = 11 } } Process { try { foreach ($U in $User) { if ($U -ceq 'me') { $U = $Script:PSGSuite.AdminEmail } elseif ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/calendar' ServiceType = 'Google.Apis.Calendar.v3.CalendarService' User = $U } $service = New-GoogleService @serviceParams if ($PSCmdlet.ParameterSetName -eq 'AttendeeEmails' -and $PSBoundParameters.Keys -contains 'AttendeeEmails') { [Google.Apis.Calendar.v3.Data.EventAttendee[]]$Attendees = $AttendeeEmails | ForEach-Object { Add-GSEventAttendee -Email $_ } } $body = New-Object 'Google.Apis.Calendar.v3.Data.Event' if ($Attendees) { $body.Attendees = [Google.Apis.Calendar.v3.Data.EventAttendee[]]$Attendees } foreach ($key in $PSBoundParameters.Keys) { switch ($key) { EventColor { $body.ColorId = $colorHash[$EventColor] } PrivateExtendedProperties { if (-not $ExtendedProperties) { $ExtendedProperties = New-Object 'Google.Apis.Calendar.v3.Data.Event+ExtendedPropertiesData' -Property @{ Private__ = (New-Object 'System.Collections.Generic.Dictionary[string,string]') Shared = (New-Object 'System.Collections.Generic.Dictionary[string,string]') } } elseif (-not $ExtendedProperties.Private__) { $ExtendedProperties.Private__ = (New-Object 'System.Collections.Generic.Dictionary[string,string]') } foreach ($prop in $PrivateExtendedProperties.Keys) { $ExtendedProperties.Private__.Add($prop,$PrivateExtendedProperties[$prop]) } } SharedExtendedProperties { if (-not $ExtendedProperties) { $ExtendedProperties = New-Object 'Google.Apis.Calendar.v3.Data.Event+ExtendedPropertiesData' -Property @{ Private__ = (New-Object 'System.Collections.Generic.Dictionary[string,string]') Shared = (New-Object 'System.Collections.Generic.Dictionary[string,string]') } } elseif (-not $ExtendedProperties.Shared) { $ExtendedProperties.Shared = (New-Object 'System.Collections.Generic.Dictionary[string,string]') } foreach ($prop in $SharedExtendedProperties.Keys) { $ExtendedProperties.Shared.Add($prop,$SharedExtendedProperties[$prop]) } } DisableReminder { $reminder = New-Object 'Google.Apis.Calendar.v3.Data.Event+RemindersData' -Property @{ UseDefault = (-not $DisableReminder) } $body.Reminders = $reminder } Default { if ($body.PSObject.Properties.Name -contains $key) { $body.$key = $PSBoundParameters[$key] } } } } if ($ExtendedProperties) { $body.ExtendedProperties = $ExtendedProperties } $body.Start = if ($UTCStartDateTime) { New-Object 'Google.Apis.Calendar.v3.Data.EventDateTime' -Property @{ DateTime = $UTCStartDateTime } } elseif ($StartDate) { New-Object 'Google.Apis.Calendar.v3.Data.EventDateTime' -Property @{ Date = (Get-Date $StartDate -Format "yyyy-MM-dd") } } elseif ($LocalStartDateTime) { New-Object 'Google.Apis.Calendar.v3.Data.EventDateTime' -Property @{ DateTime = $LocalStartDateTime } } $body.End = if ($UTCEndDateTime) { New-Object 'Google.Apis.Calendar.v3.Data.EventDateTime' -Property @{ DateTime = $UTCEndDateTime } } elseif ($EndDate) { New-Object 'Google.Apis.Calendar.v3.Data.EventDateTime' -Property @{ Date = (Get-Date $EndDate -Format "yyyy-MM-dd") } } elseif ($LocalEndDateTime) { New-Object 'Google.Apis.Calendar.v3.Data.EventDateTime' -Property @{ DateTime = $LocalEndDateTime } } foreach ($calId in $CalendarID) { foreach ($evId in $EventId) { Write-Verbose "Updating Calendar Event '$evId' on calendar '$calId' for user '$U'" $request = $service.Events.Patch($body,$calId,$evId) $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $U -PassThru | Add-Member -MemberType NoteProperty -Name 'CalendarId' -Value $calId -PassThru | Add-Member -MemberType NoteProperty -Name 'EventId' -Value $evId -PassThru } } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Update-GSCalendarEvent' function Get-GSChatMember { <# .SYNOPSIS Gets Chat member information .DESCRIPTION Gets Chat member information .PARAMETER Member Resource name of the membership to be retrieved, in the form "spaces/members". Example: spaces/AAAAMpdlehY/members/105115627578887013105 .PARAMETER Space The resource name of the space for which membership list is to be fetched, in the form "spaces". Example: spaces/AAAAMpdlehY .EXAMPLE Get-GSChatMember -Space 'spaces/AAAAMpdlehY' Gets the list of human members in the Chat space specified #> [cmdletbinding(DefaultParameterSetName = "List")] Param ( [parameter(Mandatory = $true,ParameterSetName = "Get")] [string[]] $Member, [parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true,ParameterSetName = "List")] [Alias('Parent','Name')] [string[]] $Space ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/chat.bot' ServiceType = 'Google.Apis.HangoutsChat.v1.HangoutsChatService' } $service = New-GoogleService @serviceParams } Process { switch ($PSCmdlet.ParameterSetName) { Get { foreach ($mem in $Member) { try { $request = $service.Spaces.Members.Get($mem) Write-Verbose "Getting Member '$mem'" $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } List { foreach ($sp in $Space) { try { if ($sp -notlike "spaces/*") { try { $sp = Get-GSChatConfig -SpaceName $sp -ErrorAction Stop } catch { $sp = "spaces/$sp" } } $request = $service.Spaces.Members.List($sp) Write-Verbose "Getting Member List of Chat Space '$sp'" [int]$i = 1 do { $result = $request.Execute() $result.Memberships if ($result.NextPageToken) { $request.PageToken = $result.NextPageToken } [int]$retrieved = ($i + $result.Memberships.Count) - 1 Write-Verbose "Retrieved $retrieved Memberships..." [int]$i = $i + $result.Memberships.Count } until (!$result.NextPageToken) } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } } } Export-ModuleMember -Function 'Get-GSChatMember' function Get-GSChatMessage { <# .SYNOPSIS Gets a Chat message .DESCRIPTION Gets a Chat message .PARAMETER Name Resource name of the message to be retrieved, in the form "spaces/messages". Example: spaces/AAAAMpdlehY/messages/UMxbHmzDlr4.UMxbHmzDlr4 .EXAMPLE Get-GSChatMessage -Name 'spaces/AAAAMpdlehY/messages/UMxbHmzDlr4.UMxbHmzDlr4' Gets the Chat message specified #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0)] [Alias('Id')] [string[]] $Name ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/chat.bot' ServiceType = 'Google.Apis.HangoutsChat.v1.HangoutsChatService' } $service = New-GoogleService @serviceParams } Process { foreach ($msg in $Name) { try { $request = $service.Spaces.Messages.Get($msg) Write-Verbose "Getting Message '$msg'" $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Get-GSChatMessage' function Get-GSChatSpace { <# .SYNOPSIS Gets a Chat space .DESCRIPTION Gets a Chat space .PARAMETER Space The resource name of the space for which membership list is to be fetched, in the form "spaces". If left blank, returns the list of spaces the bot is a member of Example: spaces/AAAAMpdlehY .EXAMPLE Get-GSChatSpace Gets the list of Chat spaces the bot is a member of #> [cmdletbinding()] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias("Name")] [string[]] $Space ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/chat.bot' ServiceType = 'Google.Apis.HangoutsChat.v1.HangoutsChatService' } $service = New-GoogleService @serviceParams } Process { if ($Space) { foreach ($sp in $Space) { try { if ($sp -notlike "spaces/*") { try { $sp = Get-GSChatConfig -SpaceName $sp -ErrorAction Stop } catch { $sp = "spaces/$sp" } } $request = $service.Spaces.Get($sp) Write-Verbose "Getting Chat Space '$sp'" $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } else { try { $spaceArray = @() $request = $service.Spaces.List() Write-Verbose "Getting Chat Space List" [int]$i = 1 do { $result = $request.Execute() $result.Spaces $spaceArray += $result.Spaces if ($result.NextPageToken) { $request.PageToken = $result.NextPageToken } [int]$retrieved = ($i + $result.Spaces.Count) - 1 Write-Verbose "Retrieved $retrieved Spaces..." [int]$i = $i + $result.Spaces.Count } until (!$result.NextPageToken) } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } End { Write-Verbose "Updating PSGSuite Config with Space list" $spaceHashArray = @() $spaceArray | ForEach-Object { if ($_.DisplayName) { $spaceHashArray += @{$_.DisplayName = $_.Name} } else { $member = Get-GSChatMember -Space $_.Name -Verbose:$false $id = $member.Member.Name $primaryEmail = (Get-GSUser -User ($id.Replace('users/',''))).PrimaryEmail $spaceHashArray += @{ $id = $_.Name $member.Member.DisplayName = $_.Name $primaryEmail = $_.Name } } } Set-PSGSuiteConfig -Space $spaceHashArray -Verbose:$false } } Export-ModuleMember -Function 'Get-GSChatSpace' function Remove-GSChatMessage { <# .SYNOPSIS Removes a Chat message .DESCRIPTION Removes a Chat message .PARAMETER Name Resource name of the message to be removed, in the form "spaces/messages". Example: spaces/AAAAMpdlehY/messages/UMxbHmzDlr4.UMxbHmzDlr4 .EXAMPLE Remove-GSChatMessage -Name 'spaces/AAAAMpdlehY/messages/UMxbHmzDlr4.UMxbHmzDlr4' Removes the Chat message specified after confirmation #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $true,Position = 0)] [Alias('Id')] [string[]] $Name ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/chat.bot' ServiceType = 'Google.Apis.HangoutsChat.v1.HangoutsChatService' } $service = New-GoogleService @serviceParams } Process { foreach ($msg in $Name) { try { if ($PSCmdlet.ShouldProcess("Removing Message '$msg'")) { $request = $service.Spaces.Messages.Delete($msg) $request.Execute() Write-Verbose "Successfully removed Message '$msg'" } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Remove-GSChatMessage' function Send-GSChatMessage { <# .SYNOPSIS Sends a Chat message .DESCRIPTION Sends a Chat message .PARAMETER Text Plain-text body of the message. .PARAMETER Thread The thread the message belongs to, in the form "spaces/threads". Example: spaces/AAAA3dnRkmI/threads/_EyIp5BthJk .PARAMETER FallbackText A plain-text description of the message's cards, used when the actual cards cannot be displayed (e.g. mobile notifications). .PARAMETER PreviewText Text for generating preview chips. This text will not be displayed to the user, but any links to images, web pages, videos, etc. included here will generate preview chips. .PARAMETER ActionResponseType Part of the ActionResponse. Parameters that a bot can use to configure how its response is posted. The ActionResponseType is the type of bot response. Available values are: * NEW_MESSAGE: Post as a new message in the topic. * UPDATE_MESSAGE: Update the bot's own message. (Only after CARD_CLICKED events.) * REQUEST_CONFIG: Privately ask the user for additional auth or config. .PARAMETER ActionResponseUrl Part of the ActionResponse. Parameters that a bot can use to configure how its response is posted. The ActionResponseUrl is the URL for users to auth or config. (Only for REQUEST_CONFIG response types.) .PARAMETER Parent The resource name of the space to send the message to, in the form "spaces". Example: spaces/AAAAMpdlehY .PARAMETER ThreadKey Opaque thread identifier string that can be specified to group messages into a single thread. If this is the first message with a given thread identifier, a new thread is created. Subsequent messages with the same thread identifier will be posted into the same thread. This relieves bots and webhooks from having to store the Hangouts Chat thread ID of a thread (created earlier by them) to post further updates to it. .PARAMETER Webhook The Url of the Webhook for the space to send the message to. You can safely store an encrypted dictionary of Webhooks in the PSGSuite Config by passing a hashtable to the `-Webhook` parameter, i.e.: Set-PSGSuiteConfig -Webhook @{JobReports = 'https://chat.googleapis.com/v1/spaces/xxxxxxxxxx/messages?key=xxxxxxxxxxxxxxxxxx&token=xxxxxxxxxxxxxxxxxx'} To retrieve a stored Webhook, you can use `Get-GSChatWebhook`, i.e.: Send-GSChatMessage -Text "Post job report:" -Cards $cards -Webhook (Get-GSChatWebhook JobReports) .PARAMETER MessageSegment Any Chat message segment objects created with functions named `Add-GSChat*` passed through the pipeline or added directly to this parameter as values. If section widgets are passed directly to this function, a new section without a SectionHeader will be created and the widgets will be added to it .EXAMPLE Send-GSChatMessage -Text "Post job report:" -Cards $cards -Webhook (Get-GSChatWebhook JobReports) Sends a simple Chat message using the JobReports webhook .EXAMPLE Add-GSChatTextParagraph -Text "Guys...","We <b>NEED</b> to <i>stop</i> spending money on <b>crap</b>!" | Add-GSChatKeyValue -TopLabel "Chocolate Budget" -Content '$5.00' -Icon DOLLAR | Add-GSChatKeyValue -TopLabel "Actual Spending" -Content '$5,000,000!' -BottomLabel "WTF" -Icon AIRPLANE | Add-GSChatImage -ImageUrl "https://media.tenor.com/images/f78545a9b520ecf953578b4be220f26d/tenor.gif" -LinkImage | Add-GSChatCardSection -SectionHeader "Dollar bills, y'all" -OutVariable sect1 | Add-GSChatButton -Text "Launch nuke" -OnClick (Add-GSChatOnClick -Url "https://github.com/scrthq/PSGSuite") -Verbose -OutVariable button1 | Add-GSChatButton -Text "Unleash hounds" -OnClick (Add-GSChatOnClick -Url "https://admin.google.com/?hl=en&authuser=0") -Verbose -OutVariable button2 | Add-GSChatCardSection -SectionHeader "What should we do?" -OutVariable sect2 | Add-GSChatCard -HeaderTitle "Makin' moves with" -HeaderSubtitle "DEM GOODIES" -OutVariable card | Add-GSChatTextParagraph -Text "This message sent by <b>PSGSuite</b> via WebHook!" | Add-GSChatCardSection -SectionHeader "Additional Info" -OutVariable sect2 | Send-GSChatMessage -Text "Got that report, boss:" -FallbackText "Mistakes have been made..." -Webhook ReportRoom This example shows the pipeline capabilities of the Chat functions in PSGSuite. Starting from top to bottom: 1. Add a TextParagraph widget 2. Add a KeyValue with an icon 3. Add another KeyValue with a different icon 4. Add an image and create an OnClick event to open the image's URL by using the -LinkImage parameter 5. Add a new section to encapsulate the widgets sent through the pipeline before it 6. Add a TextButton that opens the PSGSuite GitHub repo when clicked 7. Add another TextButton that opens Google Admin Console when clicked 8. Wrap the 2 buttons in a new Section to divide the content 9. Wrap all widgets and sections in the pipeline so far in a Card 10. Add a new TextParagraph as a footer to the message 11. Wrap that TextParagraph in a new section 12. Send the message and include FallbackText that's displayed in the mobile notification. Since the final TextParagraph and Section are not followed by a new Card addition, Send-GSChatMessage will create a new Card just for the remaining segments then send the completed message via Webhook. The Webhook short-name is used to reference the full URL stored in the encrypted Config so it's not displayed in the actual script. .EXAMPLE Get-Service | Select-Object -First 5 | ForEach-Object { Add-GSChatKeyValue -TopLabel $_.DisplayName -Content $_.Status -BottomLabel $_.Name -Icon TICKET } | Add-GSChatCardSection -SectionHeader "Top 5 Services" | Send-GSChatMessage -Text "Service Report:" -FallbackText "Service Report" -Webhook Reports This gets the first 5 Services returned by Get-Service, creates KeyValue widgets for each, wraps it in a section with a header, then sends it to the Reports Webhook #> [cmdletbinding(DefaultParameterSetName = "Webhook")] Param ( [parameter(Mandatory = $false,Position = 0)] [string[]] $Text, [parameter(Mandatory = $false)] [string] $Thread, [parameter(Mandatory = $false)] [string[]] $FallbackText, [parameter(Mandatory = $false)] [string] $PreviewText, [parameter(Mandatory = $false)] [ValidateSet('NEW_MESSAGE','UPDATE_MESSAGE','REQUEST_CONFIG')] [string] $ActionResponseType, [parameter(Mandatory = $false)] [string] $ActionResponseUrl, [parameter(Mandatory = $true,ParameterSetName = "SDK")] [string[]] $Parent, [parameter(Mandatory = $false,ParameterSetName = "SDK")] [parameter(Mandatory = $false,ParameterSetName = "Rest")] [string] $ThreadKey, [parameter(Mandatory = $true,ParameterSetName = "Rest")] [string[]] $RestParent, [parameter(Mandatory = $true,ParameterSetName = "Webhook")] [string[]] $Webhook, [parameter(Mandatory = $false,ValueFromPipeline = $true)] [Alias('InputObject')] [ValidateScript({ $allowedTypes = "PSGSuite.Chat.Message.Card","PSGSuite.Chat.Message.Card.Section","PSGSuite.Chat.Message.Card.CardAction","PSGSuite.Chat.Message.Card.Section.TextParagraph","PSGSuite.Chat.Message.Card.Section.Button","PSGSuite.Chat.Message.Card.Section.Image","PSGSuite.Chat.Message.Card.Section.KeyValue" foreach ($item in $_) { if ([string]$($item.PSTypeNames) -match "($(($allowedTypes|ForEach-Object{[RegEx]::Escape($_)}) -join '|'))") { $true } else { throw "This parameter only accepts the following types: $($allowedTypes -join ", "). The current types of the value are: $($item.PSTypeNames -join ", ")." } } })] [Object[]] $MessageSegment, [parameter(Mandatory = $true,ParameterSetName = "BodyPassThru")] [switch] $BodyPassThru ) Begin { $addlSections = @() $addlCardActions = @() $addlSectionWidgets = @() switch ($PSCmdlet.ParameterSetName) { default { $body = @{} foreach ($key in $PSBoundParameters.Keys) { switch ($key) { Text { $body['text'] = ($Text -join "`n") } PreviewText { $body['previewText'] = $PSBoundParameters[$key] } FallbackText { $body['fallbackText'] = ($PSBoundParameters[$key] -join "`n") } Thread { $body['thread'] = @{ name = $Thread } } ActionResponseType { if (!$body['actionResponse']) { $body['actionResponse'] = @{} } $body['actionResponse']['type'] = $PSBoundParameters[$key] } ActionResponseUrl { if (!$body['actionResponse']) { $body['actionResponse'] = @{} } $body['actionResponse']['url'] = $PSBoundParameters[$key] } } } } SDK { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/chat.bot' ServiceType = 'Google.Apis.HangoutsChat.v1.HangoutsChatService' } $service = New-GoogleService @serviceParams $body = New-Object 'Google.Apis.HangoutsChat.v1.Data.Message' foreach ($key in $PSBoundParameters.Keys) { switch ($key) { Text { $body.Text = ($PSBoundParameters[$key] -join "`n") } PreviewText { $body.PreviewText = $PSBoundParameters[$key] } FallbackText { $body.FallbackText = ($PSBoundParameters[$key] -join "`n") } Thread { $body.Thread = New-Object 'Google.Apis.HangoutsChat.v1.Data.Thread' -Property @{ Name = $Thread } } ActionResponseType { if (!$body.ActionResponse) { $body.ActionResponse = New-Object 'Google.Apis.HangoutsChat.v1.Data.ActionResponse' } $body.ActionResponse.Type = $PSBoundParameters[$key] } ActionResponseUrl { if (!$body.ActionResponse) { $body.ActionResponse = New-Object 'Google.Apis.HangoutsChat.v1.Data.ActionResponse' } $body.ActionResponse.Url = $PSBoundParameters[$key] } } } } } } Process { foreach ($segment in $MessageSegment) { switch -RegEx ($segment['SDK'].PSTypeNames[0]) { '(.*?)Google\.Apis\.HangoutsChat\.v1\.Data\.Card' { switch ($PSCmdlet.ParameterSetName) { default { if (!$body['cards']) { $body['cards'] = @() } $body['cards'] += $segment['Webhook'] } SDK { if (!$body.Cards) { $body.Cards = New-Object 'System.Collections.Generic.List[Google.Apis.HangoutsChat.v1.Data.Card]' } $body.Cards.Add($segment['SDK']) | Out-Null } } } '(.*?)Google\.Apis\.HangoutsChat\.v1\.Data\.Section' { $addlSections += $segment } '(.*?)Google\.Apis\.HangoutsChat\.v1\.Data\.CardAction' { $addlCardActions += $segment } default { Write-Verbose "Matched a $($segment['SDK'].PSTypeNames[0]) in the MessageSegments!" $addlSectionWidgets += $segment } } } } End { switch ($PSCmdlet.ParameterSetName) { default { if ($addlCardActions -or $addlSections -or $addlSectionWidgets) { if (!$body['cards']) { $cardless = $true $body['cards'] = @() } if ($addlSections) { $body['cards'] += ($addlSections | Add-GSChatCard)['Webhook'] $cardless = $false } if ($addlSectionWidgets) { if ($cardless) { $body['cards'] += ($addlSectionWidgets | Add-GSChatCardSection | Add-GSChatCard)['Webhook'] } else { $newSection = ($addlSectionWidgets | Add-GSChatCardSection)['Webhook'] if (!$body['cards'][-1]['sections']) { $body['cards'][-1]['sections'] = @() } $body['cards'][-1]['sections'] += $newSection } $cardless = $false } if ($addlCardActions) { if ($cardless) { $body['cards'] += ($addlCardActions | Add-GSChatCard)['Webhook'] } elseif (!$body['cards'][-1]['cardActions']) { $body['cards'][-1]['cardActions'] = @() } foreach ($cardAction in $addlCardActions) { $body['cards'][-1]['cardActions'] += $cardAction['Webhook'] } } } switch ($PSCmdlet.ParameterSetName) { Webhook { $body = $body | ConvertTo-Json -Depth 15 foreach ($hook in $Webhook) { try { if ($hook -notlike "https://chat.googleapis.com/v1/spaces/*") { $hook = Get-GSChatConfig -WebhookName $hook -ErrorAction Stop } Write-Verbose "Sending Chat Message via Webhook to '$($hook -replace "\?key\=.*",'')'" Invoke-RestMethod -Method Post -Uri ([Uri]$hook) -Body $body -ContentType 'application/json' -Verbose:$false | Add-Member -MemberType NoteProperty -Name 'Webhook' -Value $hook -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Rest { $body = $body | ConvertTo-Json -Depth 15 foreach ($restPar in $RestParent) { try { if ($restPar -notlike "spaces/*") { try { $restPar = Get-GSChatConfig -SpaceName $restPar -ErrorAction Stop } catch { $restPar = "spaces/$restPar" } } $header = @{ Authorization = "Bearer $(Get-GSToken -Scopes "https://www.googleapis.com/auth/chat.bot" -Verbose:$false)" } $hook = "https://chat.googleapis.com/v1/$($restPar)/messages" if ($PSBoundParameters.Keys -contains 'ThreadKey') { $hook = "$($hook)?threadKey=$ThreadKey" $addlText = " in ThreadKey '$ThreadKey'" } else { $addlText = "" } Write-Verbose "Sending Chat Message via REST API to parent '$restPar'$addlText" Invoke-RestMethod -Method Post -Uri ([Uri]$hook) -Headers $header -Body $body -ContentType 'application/json' -Verbose:$false | Add-Member -MemberType NoteProperty -Name 'RestParent' -Value $restPar -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } BodyPassThru { $newBody = @{ token = (Get-GSToken -Scopes "https://www.googleapis.com/auth/chat.bot" -Verbose:$false) body = $body } $newBody = $newBody | ConvertTo-Json -Depth 20 -Compress return $newBody } } } SDK { if ($addlCardActions -or $addlSections -or $addlSectionWidgets) { if (!$body.Cards) { $cardless = $true $body.Cards = New-Object 'System.Collections.Generic.List[Google.Apis.HangoutsChat.v1.Data.Card]' } if ($addlSections) { $body.Cards.Add(($addlSections | Add-GSChatCard)['SDK']) | Out-Null $cardless = $false } if ($addlSectionWidgets) { if ($cardless) { $body.Cards.Add(($addlSectionWidgets | Add-GSChatCardSection | Add-GSChatCard)['SDK']) | Out-Null } else { $newSection = ($addlSectionWidgets | Add-GSChatCardSection)['SDK'] if (!$body.Cards[-1].Sections) { $body.Cards[-1].Sections = New-Object 'System.Collections.Generic.List[Google.Apis.HangoutsChat.v1.Data.Section]' } $body.Cards[-1].Sections.Add($newSection) | Out-Null } $cardless = $false } if ($addlCardActions) { if ($cardless) { $body.Cards.Add(($addlCardActions | Add-GSChatCard)['SDK']) } elseif (!$body.Cards[-1].CardActions) { $body.Cards[-1].CardActions = New-Object 'System.Collections.Generic.List[Google.Apis.HangoutsChat.v1.Data.CardAction]' } foreach ($cardAction in $addlCardActions) { $body.Cards[-1].CardActions.Add($cardAction['SDK']) | Out-Null } } } foreach ($par in $Parent){ try { if ($par -notlike "spaces/*") { try { $par = Get-GSChatConfig -SpaceName $par -ErrorAction Stop } catch { $par = "spaces/$par" } } $request = $service.Spaces.Messages.Create($body,$par) if ($PSBoundParameters.Keys -contains 'ThreadKey') { $request.ThreadKey = $ThreadKey $addlText = " in ThreadKey '$ThreadKey'" } else { $addlText = "" } Write-Verbose "Sending Chat Message via SDK to Space '$par'$addlText" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'Parent' -Value $par -PassThru | Add-Member -MemberType NoteProperty -Name 'ThreadKey' -Value $ThreadKey -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } } } Export-ModuleMember -Function 'Send-GSChatMessage' function Update-GSChatMessage { <# .SYNOPSIS Updates a Chat message, i.e. for a CardClicked response .DESCRIPTION Updates a Chat message, i.e. for a CardClicked response .PARAMETER MessageId Resource name, in the form "spaces/messages". Example: spaces/89L51AAAAAE/messages/kbZTbcol8H4.kbZTbcol8H4 .PARAMETER UpdateMask Required. The field paths to be updated. Currently supported field paths: "text", "cards". .PARAMETER Text Plain-text body of the message. .PARAMETER FallbackText A plain-text description of the message's cards, used when the actual cards cannot be displayed (e.g. mobile notifications). .PARAMETER PreviewText Text for generating preview chips. This text will not be displayed to the user, but any links to images, web pages, videos, etc. included here will generate preview chips. .PARAMETER ActionResponseType Part of the ActionResponse. Parameters that a bot can use to configure how its response is posted. The ActionResponseType is the type of bot response. Available values are: * NEW_MESSAGE: Post as a new message in the topic. * UPDATE_MESSAGE: Update the bot's own message. (Only after CARD_CLICKED events.) * REQUEST_CONFIG: Privately ask the user for additional auth or config. .PARAMETER ActionResponseUrl Part of the ActionResponse. Parameters that a bot can use to configure how its response is posted. The ActionResponseUrl is the URL for users to auth or config. (Only for REQUEST_CONFIG response types.) .PARAMETER MessageSegment Any Chat message segment objects created with functions named `Add-GSChat*` passed through the pipeline or added directly to this parameter as values. If section widgets are passed directly to this function, a new section without a SectionHeader will be created and the widgets will be added to it .EXAMPLE Send-GSChatMessage -Text "Post job report:" -Cards $cards -Webhook (Get-GSChatWebhook JobReports) Sends a simple Chat message using the JobReports webhook .EXAMPLE Add-GSChatTextParagraph -Text "Guys...","We <b>NEED</b> to <i>stop</i> spending money on <b>crap</b>!" | Add-GSChatKeyValue -TopLabel "Chocolate Budget" -Content '$5.00' -Icon DOLLAR | Add-GSChatKeyValue -TopLabel "Actual Spending" -Content '$5,000,000!' -BottomLabel "WTF" -Icon AIRPLANE | Add-GSChatImage -ImageUrl "https://media.tenor.com/images/f78545a9b520ecf953578b4be220f26d/tenor.gif" -LinkImage | Add-GSChatCardSection -SectionHeader "Dollar bills, y'all" -OutVariable sect1 | Add-GSChatButton -Text "Launch nuke" -OnClick (Add-GSChatOnClick -Url "https://github.com/scrthq/PSGSuite") -Verbose -OutVariable button1 | Add-GSChatButton -Text "Unleash hounds" -OnClick (Add-GSChatOnClick -Url "https://admin.google.com/?hl=en&authuser=0") -Verbose -OutVariable button2 | Add-GSChatCardSection -SectionHeader "What should we do?" -OutVariable sect2 | Add-GSChatCard -HeaderTitle "Makin' moves with" -HeaderSubtitle "DEM GOODIES" -OutVariable card | Add-GSChatTextParagraph -Text "This message sent by <b>PSGSuite</b> via WebHook!" | Add-GSChatCardSection -SectionHeader "Additional Info" -OutVariable sect2 | Send-GSChatMessage -Text "Got that report, boss:" -FallbackText "Mistakes have been made..." -Webhook ReportRoom This example shows the pipeline capabilities of the Chat functions in PSGSuite. Starting from top to bottom: 1. Add a TextParagraph widget 2. Add a KeyValue with an icon 3. Add another KeyValue with a different icon 4. Add an image and create an OnClick event to open the image's URL by using the -LinkImage parameter 5. Add a new section to encapsulate the widgets sent through the pipeline before it 6. Add a TextButton that opens the PSGSuite GitHub repo when clicked 7. Add another TextButton that opens Google Admin Console when clicked 8. Wrap the 2 buttons in a new Section to divide the content 9. Wrap all widgets and sections in the pipeline so far in a Card 10. Add a new TextParagraph as a footer to the message 11. Wrap that TextParagraph in a new section 12. Send the message and include FallbackText that's displayed in the mobile notification. Since the final TextParagraph and Section are not followed by a new Card addition, Send-GSChatMessage will create a new Card just for the remaining segments then send the completed message via Webhook. The Webhook short-name is used to reference the full URL stored in the encrypted Config so it's not displayed in the actual script. .EXAMPLE Get-Service | Select-Object -First 5 | ForEach-Object { Add-GSChatKeyValue -TopLabel $_.DisplayName -Content $_.Status -BottomLabel $_.Name -Icon TICKET } | Add-GSChatCardSection -SectionHeader "Top 5 Services" | Send-GSChatMessage -Text "Service Report:" -FallbackText "Service Report" -Webhook Reports This gets the first 5 Services returned by Get-Service, creates KeyValue widgets for each, wraps it in a section with a header, then sends it to the Reports Webhook #> [cmdletbinding(DefaultParameterSetName = "Update")] Param ( [parameter(Mandatory = $true,Position = 0,ParameterSetName = "Update")] [string] $MessageId, [parameter(Mandatory = $true,ParameterSetName = "BodyPassThru")] [switch] $BodyPassThru, [parameter(Mandatory = $false,Position = 1)] [ValidateSet("text","cards")] [string[]] $UpdateMask, [parameter(Mandatory = $false)] [string[]] $Text, [parameter(Mandatory = $false)] [string[]] $FallbackText, [parameter(Mandatory = $false)] [string] $PreviewText, [parameter(Mandatory = $false)] [ValidateSet('NEW_MESSAGE','UPDATE_MESSAGE','REQUEST_CONFIG')] [string] $ActionResponseType = 'UPDATE_MESSAGE', [parameter(Mandatory = $false)] [string] $ActionResponseUrl, [parameter(Mandatory = $false,ParameterSetName = "Update")] [switch] $UseRest, [parameter(Mandatory = $false,ValueFromPipeline = $true)] [Alias('InputObject')] [ValidateScript({ $allowedTypes = "PSGSuite.Chat.Message.Card","PSGSuite.Chat.Message.Card.Section","PSGSuite.Chat.Message.Card.CardAction","PSGSuite.Chat.Message.Card.Section.TextParagraph","PSGSuite.Chat.Message.Card.Section.Button","PSGSuite.Chat.Message.Card.Section.Image","PSGSuite.Chat.Message.Card.Section.KeyValue" foreach ($item in $_) { if ([string]$($item.PSTypeNames) -match "($(($allowedTypes|ForEach-Object{[RegEx]::Escape($_)}) -join '|'))") { $true } else { throw "This parameter only accepts the following types: $($allowedTypes -join ", "). The current types of the value are: $($item.PSTypeNames -join ", ")." } } })] [Object[]] $MessageSegment ) Begin { $addlSections = @() $addlCardActions = @() $addlSectionWidgets = @() if ($UseRest -or $BodyPassThru) { $body = @{ actionResponse = @{ type = $ActionResponseType } } foreach ($key in $PSBoundParameters.Keys) { switch ($key) { Text { if ($UpdateMask -notcontains 'text') { $UpdateMask += 'text' } $body['text'] = ($Text -join "`n") } PreviewText { $body['previewText'] = $PSBoundParameters[$key] } FallbackText { $body['fallbackText'] = ($PSBoundParameters[$key] -join "`n") } ActionResponseUrl { if (!$body['actionResponse']) { $body['actionResponse'] = @{} } $body['actionResponse']['url'] = $PSBoundParameters[$key] } } } } else { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/chat.bot' ServiceType = 'Google.Apis.HangoutsChat.v1.HangoutsChatService' } $service = New-GoogleService @serviceParams $body = New-Object 'Google.Apis.HangoutsChat.v1.Data.Message' $body.ActionResponse = New-Object 'Google.Apis.HangoutsChat.v1.Data.ActionResponse' $body.ActionResponse.Type = $ActionResponseType foreach ($key in $PSBoundParameters.Keys) { switch ($key) { Text { if ($UpdateMask -notcontains 'text') { $UpdateMask += 'text' } $body.Text = ($PSBoundParameters[$key] -join "`n") } PreviewText { $body.PreviewText = $PSBoundParameters[$key] } FallbackText { $body.FallbackText = ($PSBoundParameters[$key] -join "`n") } ActionResponseUrl { if (!$body.ActionResponse) { $body.ActionResponse = New-Object 'Google.Apis.HangoutsChat.v1.Data.ActionResponse' } $body.ActionResponse.Url = $PSBoundParameters[$key] } } } } } Process { if ($MessageSegment) { if ($UpdateMask -notcontains 'cards') { $UpdateMask += 'cards' } foreach ($segment in $MessageSegment) { switch -RegEx ($segment['SDK'].PSTypeNames[0]) { '(.*?)Google\.Apis\.HangoutsChat\.v1\.Data\.Card' { if ($UseRest -or $BodyPassThru) { if (!$body['cards']) { $body['cards'] = @() } $body['cards'] += $segment['Webhook'] } else { if (!$body.Cards) { $body.Cards = New-Object 'System.Collections.Generic.List[Google.Apis.HangoutsChat.v1.Data.Card]' } $body.Cards.Add($segment['SDK']) | Out-Null } } '(.*?)Google\.Apis\.HangoutsChat\.v1\.Data\.Section' { $addlSections += $segment } '(.*?)Google\.Apis\.HangoutsChat\.v1\.Data\.CardAction' { $addlCardActions += $segment } default { Write-Verbose "Matched a $($segment['SDK'].PSTypeNames[0]) in the MessageSegments!" $addlSectionWidgets += $segment } } } } } End { if ($UseRest -or $BodyPassThru) { if ($addlCardActions -or $addlSections -or $addlSectionWidgets) { if (!$body['cards']) { $cardless = $true $body['cards'] = @() } if ($addlSections) { $body['cards'] += ($addlSections | Add-GSChatCard)['Webhook'] $cardless = $false } if ($addlSectionWidgets) { if ($cardless) { $body['cards'] += ($addlSectionWidgets | Add-GSChatCardSection | Add-GSChatCard)['Webhook'] } else { $newSection = ($addlSectionWidgets | Add-GSChatCardSection)['Webhook'] if (!$body['cards'][-1]['sections']) { $body['cards'][-1]['sections'] = @() } $body['cards'][-1]['sections'] += $newSection } $cardless = $false } if ($addlCardActions) { if ($cardless) { $body['cards'] += ($addlCardActions | Add-GSChatCard)['Webhook'] } elseif (!$body['cards'][-1]['cardActions']) { $body['cards'][-1]['cardActions'] = @() } foreach ($cardAction in $addlCardActions) { $body['cards'][-1]['cardActions'] += $cardAction['Webhook'] } } } if ($BodyPassThru) { $newBody = @{ token = (Get-GSToken -Scopes "https://www.googleapis.com/auth/chat.bot" -Verbose:$false) body = $body updateMask = ($UpdateMask -join ',') } $newBody = $newBody | ConvertTo-Json -Depth 20 -Compress return $newBody } else { $body = $body | ConvertTo-Json -Depth 20 try { $header = @{ Authorization = "Bearer $(Get-GSToken -P12KeyPath $Script:PSGSuite.P12KeyPath -Scopes "https://www.googleapis.com/auth/chat.bot" -AppEmail $Script:PSGSuite.AppEmail -AdminEmail $Script:PSGSuite.AdminEmail -Verbose:$false)" } $hook = "https://chat.googleapis.com/v1/$($MessageId)?updateMask=$($UpdateMask -join ',')" Write-Verbose "Updating Chat Message via REST API to parent '$MessageId'" Invoke-RestMethod -Method Put -Uri ([Uri]$hook) -Headers $header -Body $body -ContentType 'application/json' -Verbose:$false | Add-Member -MemberType NoteProperty -Name 'MessageId' -Value $MessageId -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } else { if ($addlCardActions -or $addlSections -or $addlSectionWidgets) { if (!$body.Cards) { $cardless = $true $body.Cards = New-Object 'System.Collections.Generic.List[Google.Apis.HangoutsChat.v1.Data.Card]' } if ($addlSections) { $body.Cards.Add(($addlSections | Add-GSChatCard)['SDK']) | Out-Null $cardless = $false } if ($addlSectionWidgets) { if ($cardless) { $body.Cards.Add(($addlSectionWidgets | Add-GSChatCardSection | Add-GSChatCard)['SDK']) | Out-Null } else { $newSection = ($addlSectionWidgets | Add-GSChatCardSection)['SDK'] if (!$body.Cards[-1].Sections) { $body.Cards[-1].Sections = New-Object 'System.Collections.Generic.List[Google.Apis.HangoutsChat.v1.Data.Section]' } $body.Cards[-1].Sections.Add($newSection) | Out-Null } $cardless = $false } if ($addlCardActions) { if ($cardless) { $body.Cards.Add(($addlCardActions | Add-GSChatCard)['SDK']) } elseif (!$body.Cards[-1].CardActions) { $body.Cards[-1].CardActions = New-Object 'System.Collections.Generic.List[Google.Apis.HangoutsChat.v1.Data.CardAction]' } foreach ($cardAction in $addlCardActions) { $body.Cards[-1].CardActions.Add($cardAction['SDK']) | Out-Null } } } try { $request = $service.Spaces.Messages.Update($body,$MessageId) $request.UpdateMask = $UpdateMask Write-Verbose "Updating Chat Message Id '$MessageId'" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'MessageId' -Value $MessageId -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Update-GSChatMessage' function Add-GSCourseParticipant { <# .SYNOPSIS Adds students and/or teachers to a course .DESCRIPTION Adds students and/or teachers to a course .PARAMETER CourseId Identifier of the course to add participants to. This identifier can be either the Classroom-assigned identifier or an alias. .PARAMETER Student Identifier of the user. This identifier can be one of the following: * the numeric identifier for the user * the email address of the user * the string literal "me", indicating the requesting user .PARAMETER Teacher Identifier of the user. This identifier can be one of the following: * the numeric identifier for the user * the email address of the user * the string literal "me", indicating the requesting user .PARAMETER User The user to authenticate the request as .EXAMPLE Add-GSCourseParticipant -CourseId 'architecture-101' -Student plato@athens.edu,aristotle@athens.edu -Teacher zeus@athens.edu #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true)] [String] $CourseId, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias('PrimaryEmail','Email','Mail')] [String[]] $Student, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [String[]] $Teacher, [parameter(Mandatory = $false)] [String] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/classroom.rosters' ServiceType = 'Google.Apis.Classroom.v1.ClassroomService' User = $User } $service = New-GoogleService @serviceParams } Process { foreach ($part in $Student | Where-Object {-not [String]::IsNullOrEmpty($_)}) { try { $body = New-Object 'Google.Apis.Classroom.v1.Data.Student' if ( -not ($part -as [decimal])) { if ($part -ceq 'me') { $part = $Script:PSGSuite.AdminEmail } elseif ($part -notlike "*@*.*") { $part = "$($part)@$($Script:PSGSuite.Domain)" } } $body.UserId = $part Write-Verbose "Adding Student '$part' to Course '$CourseId'" $request = $service.Courses.Students.Create($body,$CourseId) $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } foreach ($part in $Teacher | Where-Object {-not [String]::IsNullOrEmpty($_)}) { try { $body = New-Object 'Google.Apis.Classroom.v1.Data.Teacher' try { [decimal]$part | Out-Null } catch { if ($part -ceq 'me') { $part = $Script:PSGSuite.AdminEmail } elseif ($part -notlike "*@*.*") { $part = "$($part)@$($Script:PSGSuite.Domain)" } } $body.UserId = $part Write-Verbose "Adding Teacher '$part' to Course '$CourseId'" $request = $service.Courses.Teachers.Create($body,$CourseId) $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Add-GSCourseParticipant' function Confirm-GSCourseInvitation { <# .SYNOPSIS Accepts an invitation, removing it and adding the invited user to the teachers or students (as appropriate) of the specified course. Only the invited user may accept an invitation. .DESCRIPTION Accepts an invitation, removing it and adding the invited user to the teachers or students (as appropriate) of the specified course. Only the invited user may accept an invitation. .PARAMETER Id Identifier of the invitation to accept. .PARAMETER User Email or email name part of the invited user. .EXAMPLE Confirm-GSCourseInvitation -Id $inviteId -User aristotle@athens.edu #> [cmdletbinding()] Param ( [parameter(Mandatory = $true)] [String] $Id, [parameter(Mandatory = $true)] [String] $User ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/classroom.rosters' ServiceType = 'Google.Apis.Classroom.v1.ClassroomService' User = $User } $service = New-GoogleService @serviceParams } Process { try { Write-Verbose "Accepting Invitation '$Id' for user '$User'" $request = $service.Invitations.Accept($Id) $request.Execute() Write-Verbose "The Invitation has been successfully accepted for user '$User'" } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Confirm-GSCourseInvitation' function Get-GSClassroomUserProfile { <# .SYNOPSIS Gets a classroom user profile .DESCRIPTION Gets a classroom user profile .PARAMETER UserId Identifier of the profile to return. The identifier can be one of the following: * the numeric identifier for the user * the email address of the user * the string literal "me", indicating the requesting user .PARAMETER Fields The specific fields to fetch .EXAMPLE Get-GSClassroomUserProfile -UserId aristotle@athens.edu #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias('Id','PrimaryEmail','Mail','UserKey')] [ValidateNotNullOrEmpty()] [String[]] $UserId, [parameter(Mandatory = $false)] [String[]] $Fields = '*' ) Begin { $serviceParams = @{ Scope = @( 'https://www.googleapis.com/auth/classroom.rosters' 'https://www.googleapis.com/auth/classroom.profile.emails' 'https://www.googleapis.com/auth/classroom.profile.photos' ) ServiceType = 'Google.Apis.Classroom.v1.ClassroomService' } $service = New-GoogleService @serviceParams } Process { foreach ($part in $UserId) { try { if ( -not ($part -as [decimal])) { if ($part -ceq 'me') { $part = $Script:PSGSuite.AdminEmail } elseif ($part -notlike "*@*.*") { $part = "$($part)@$($Script:PSGSuite.Domain)" } } Write-Verbose "Getting Classroom User Profile for '$part'" $request = $service.UserProfiles.Get($part) $request.Fields = "$($Fields -join ",")" $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Get-GSClassroomUserProfile' function Get-GSCourse { <# .SYNOPSIS Gets a classroom course or list of courses .DESCRIPTION Gets a classroom course or list of courses .PARAMETER Id Identifier of the course to return. This identifier can be either the Classroom-assigned identifier or an alias. If excluded, returns the list of courses. .PARAMETER Teacher Restricts returned courses to those having a teacher with the specified identifier. The identifier can be one of the following: * the numeric identifier for the user * the email address of the user * the string literal "me", indicating the requesting user .PARAMETER Student Restricts returned courses to those having a student with the specified identifier. The identifier can be one of the following: * the numeric identifier for the user * the email address of the user * the string literal "me", indicating the requesting user .PARAMETER CourseStates Restricts returned courses to those in one of the specified states. .PARAMETER User The user to authenticate the request as .EXAMPLE Get-GSCourse -Teacher aristotle@athens.edu #> [cmdletbinding(DefaultParameterSetName = "List")] Param ( [parameter(Mandatory = $false,Position = 0,ParameterSetName = "Get")] [ValidateNotNullOrEmpty()] [String[]] $Id, [parameter(Mandatory = $false,ParameterSetName = "List")] [String] $Teacher, [parameter(Mandatory = $false,ParameterSetName = "List")] [String] $Student, [parameter(Mandatory = $false,ParameterSetName = "List")] [Google.Apis.Classroom.v1.CoursesResource+ListRequest+CourseStatesEnum[]] $CourseStates, [parameter(Mandatory = $false)] [String] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/classroom.courses' ServiceType = 'Google.Apis.Classroom.v1.ClassroomService' User = $User } $service = New-GoogleService @serviceParams } Process { if ($PSBoundParameters.Keys -contains 'Id') { foreach ($I in $Id) { try { Write-Verbose "Getting Course '$Id'" $request = $service.Courses.Get($Id) $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } else { try { Write-Verbose "Getting Course List" $request = $service.Courses.List() foreach ($s in $CourseStates) { $request.CourseStates += $s } if ($PSBoundParameters.Keys -contains 'Student') { $request.StudentId = $PSBoundParameters['Student'] } if ($PSBoundParameters.Keys -contains 'Teacher') { $request.TeacherId = $PSBoundParameters['Teacher'] } [int]$i = 1 do { $result = $request.Execute() if ($null -ne $result.Courses) { $result.Courses } $request.PageToken = $result.NextPageToken [int]$retrieved = ($i + $result.Courses.Count) - 1 Write-Verbose "Retrieved $retrieved Courses..." [int]$i = $i + $result.Courses.Count } until (!$result.NextPageToken) } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Get-GSCourse' function Get-GSCourseAlias { <# .SYNOPSIS Gets the list of aliases for a course. .DESCRIPTION Gets the list of aliases for a course. .PARAMETER CourseId Identifier of the course to alias. This identifier can be either the Classroom-assigned identifier or an alias. .PARAMETER User The user to authenticate the request as .EXAMPLE Get-GSCourseAlias -CourseId 'architecture-101' #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0)] [String] $CourseId, [parameter(Mandatory = $false)] [String] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/classroom.courses' ServiceType = 'Google.Apis.Classroom.v1.ClassroomService' User = $User } $service = New-GoogleService @serviceParams } Process { try { Write-Verbose "Getting Alias list for Course '$CourseId'" $request = $service.Courses.Aliases.List($CourseId) $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSCourseAlias' function Get-GSCourseInvitation { <# .SYNOPSIS Gets a course invitation or list of invitations .DESCRIPTION Gets a course invitation or list of invitations .PARAMETER Id Identifier of the invitation to return. .PARAMETER CourseId Restricts returned invitations to those for a course with the specified identifier. This identifier can be either the Classroom-assigned identifier or an alias. .PARAMETER UserId Restricts returned invitations to those for a specific user. The identifier can be one of the following: * the numeric identifier for the user * the email address of the user * the string literal "me", indicating the requesting user .PARAMETER User The user to authenticate the request as .EXAMPLE Get-GSCourseInvitation -CourseId philosophy-101 #> [cmdletbinding(DefaultParameterSetName = "List")] Param ( [parameter(Mandatory = $true,ParameterSetName = "Get")] [String[]] $Id, [parameter(Mandatory = $false,ParameterSetName = "List")] [String] $CourseId, [parameter(Mandatory = $false,ParameterSetName = "List")] [String] $UserId, [parameter(Mandatory = $false)] [String] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/classroom.rosters' ServiceType = 'Google.Apis.Classroom.v1.ClassroomService' User = $User } $service = New-GoogleService @serviceParams } Process { switch ($PSCmdlet.ParameterSetName) { Get { foreach ($part in $Id) { try { Write-Verbose "Getting Invitation ID '$part'" $request = $service.Invitations.Get($part) $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } List { try { if ($PSBoundParameters.Keys -notcontains 'CourseId' -and $PSBoundParameters.Keys -notcontains 'UserId') { Write-Error "You must specify a CourseId and/or a UserId!" } else { $request = $service.Invitations.List() $verbMsg = "" if ($PSBoundParameters.Keys -contains 'CourseId') { $verbMsg += " [Course: $CourseId]" $request.CourseId = $CourseId } if ($PSBoundParameters.Keys -contains 'UserId') { if ( -not ($UserId -as [decimal])) { if ($UserId -ceq 'me') { $UserId = $Script:PSGSuite.AdminEmail } elseif ($UserId -notlike "*@*.*") { $UserId = "$($UserId)@$($Script:PSGSuite.Domain)" } } $verbMsg += " [User: $UserId]" $request.UserId = $UserId } Write-Verbose "Getting List of Invitations for$($verbMsg)" [int]$retrieved = 0 [int]$i = 1 do { $result = $request.Execute() if ($null -ne $result.Invitations) { $result.Invitations } [int]$retrieved = ($i + $result.Invitations.Count) - 1 [int]$i = $i + $result.Invitations.Count $request.PageToken = $result.NextPageToken Write-Verbose "Retrieved $retrieved Invitations..." } until (!$result.NextPageToken) } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } } Export-ModuleMember -Function 'Get-GSCourseInvitation' function Get-GSCourseParticipant { <# .SYNOPSIS Gets a course participant or list of participants (teachers/students) .DESCRIPTION Gets a course participant or list of participants (teachers/students) .PARAMETER CourseId Identifier of the course to get participants of. This identifier can be either the Classroom-assigned identifier or an alias. .PARAMETER Role The Role for which you would like to list participants for. Available values are: * Student * Teacher The default value for this parameter is @('Teacher','Student') .PARAMETER Teacher Restricts returned courses to those having a teacher with the specified identifier. The identifier can be one of the following: * the numeric identifier for the user * the email address of the user * the string literal "me", indicating the requesting user .PARAMETER Student Restricts returned courses to those having a student with the specified identifier. The identifier can be one of the following: * the numeric identifier for the user * the email address of the user * the string literal "me", indicating the requesting user .PARAMETER User The user to authenticate the request as .PARAMETER Fields The specific fields to fetch .EXAMPLE Get-GSCourseParticipant -Teacher aristotle@athens.edu #> [cmdletbinding(DefaultParameterSetName = "List")] Param ( [parameter(Mandatory = $true,Position = 0)] [ValidateNotNullOrEmpty()] [String] $CourseId, [parameter(Mandatory = $false,ParameterSetName = "List")] [ValidateSet('Teacher','Student')] [String[]] $Role = @('Teacher','Student'), [parameter(Mandatory = $false,ParameterSetName = "Get")] [String[]] $Teacher, [parameter(Mandatory = $false,ParameterSetName = "Get")] [String[]] $Student, [parameter(Mandatory = $false)] [String] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false)] [String[]] $Fields = '*' ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = @( 'https://www.googleapis.com/auth/classroom.rosters' 'https://www.googleapis.com/auth/classroom.profile.emails' 'https://www.googleapis.com/auth/classroom.profile.photos' ) ServiceType = 'Google.Apis.Classroom.v1.ClassroomService' User = $User } $service = New-GoogleService @serviceParams } Process { switch ($PSCmdlet.ParameterSetName) { Get { foreach ($part in $Student) { try { if ( -not ($part -as [decimal])) { if ($part -ceq 'me') { $part = $Script:PSGSuite.AdminEmail } elseif ($part -notlike "*@*.*") { $part = "$($part)@$($Script:PSGSuite.Domain)" } } Write-Verbose "Getting Student '$part' for Course '$CourseId'" $request = $service.Courses.Students.Get($CourseId,$part) $request.Fields = "$($Fields -join ",")" $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } foreach ($part in $Teacher) { try { try { [decimal]$part | Out-Null } catch { if ($part -ceq 'me') { $part = $Script:PSGSuite.AdminEmail } elseif ($part -notlike "*@*.*") { $part = "$($part)@$($Script:PSGSuite.Domain)" } } Write-Verbose "Getting Teacher '$part' for Course '$CourseId'" $request = $service.Courses.Teachers.Get($CourseId,$part) $request.Fields = "$($Fields -join ",")" $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } List { foreach ($Ro in $Role) { try { Write-Verbose "Getting List of $($Ro)s for Course '$CourseId'" $request = switch ($Ro) { Teacher { $service.Courses.Teachers.List($CourseId) } Student { $service.Courses.Students.List($CourseId) } } $request.Fields = "$($Fields -join ",")" [int]$retrieved = 0 [int]$i = 1 do { $result = $request.Execute() switch ($Ro) { Teacher { if ($null -ne $result.Teachers) { $result.Teachers } [int]$retrieved = ($i + $result.Teachers.Count) - 1 [int]$i = $i + $result.Teachers.Count } Student { if ($null -ne $result.Students) { $result.Students } [int]$retrieved = ($i + $result.Students.Count) - 1 [int]$i = $i + $result.Students.Count } } $request.PageToken = $result.NextPageToken Write-Verbose "Retrieved $retrieved $($Ro)s..." } until (!$result.NextPageToken) } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } } } Export-ModuleMember -Function 'Get-GSCourseParticipant' function Get-GSStudentGuardian { <# .SYNOPSIS Gets a guardian or list of guardians for a student. .DESCRIPTION Gets a guardian or list of guardians for a student. .PARAMETER StudentId The identifier of the student to get guardian info for. The identifier can be one of the following: * the numeric identifier for the user * the email address of the user * the string literal "me", indicating the requesting user * the string literal "-", indicating that results should be returned for all students that the requesting user is permitted to view guardians for. [Default] * **This is only allowed when excluding the `GuardianId` parameter to perform a List request!** .PARAMETER GuardianId The id field from a Guardian. .PARAMETER User The user to authenticate the request as .EXAMPLE Get-GSStudentGuardian Gets the list of guardians for all students. #> [cmdletbinding()] Param ( [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias('Student')] [String[]] $StudentId = "-", [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias('Guardian')] [String[]] $GuardianId, [parameter(Mandatory = $false)] [String] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/classroom.guardianlinks.students' ServiceType = 'Google.Apis.Classroom.v1.ClassroomService' User = $User } $service = New-GoogleService @serviceParams } Process { foreach ($stuId in $StudentId) { try { if ($stuId -ne '-') { if ( -not ($stuId -as [decimal])) { if ($stuId -ceq 'me') { $stuId = $Script:PSGSuite.AdminEmail } elseif ($stuId -notlike "*@*.*") { $stuId = "$($stuId)@$($Script:PSGSuite.Domain)" } } } elseif ($PSBoundParameters.Keys -contains 'GuardianId') { Write-Error "You must specify a valid StudentId when including a GuardianId! Current value '$stuId'" } if ($PSBoundParameters.Keys -contains 'GuardianId') { foreach ($guard in $GuardianId) { try { Write-Verbose "Getting Guardian '$guard' for Student '$stuId'" $request = $service.UserProfiles.Guardians.Get($stuId,$guard) $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } else { try { if ($stuId -eq '-') { Write-Verbose "Listing all Guardians" } else { Write-Verbose "Listing Guardians for Student '$stuId'" } $request = $service.UserProfiles.Guardians.List($stuId) [int]$i = 1 do { $result = $request.Execute() if ($null -ne $result.Guardians) { $result.Guardians } $request.PageToken = $result.NextPageToken [int]$retrieved = ($i + $result.Guardians.Count) - 1 Write-Verbose "Retrieved $retrieved Guardians..." [int]$i = $i + $result.Guardians.Count } until (!$result.NextPageToken) } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Get-GSStudentGuardian' function Get-GSStudentGuardianInvitation { <# .SYNOPSIS Gets a guardian invitation or list of guardian invitations. .DESCRIPTION Gets a guardian invitation or list of guardian invitations. .PARAMETER InvitationId The id field of the GuardianInvitation being requested. .PARAMETER StudentId The identifier of the student whose guardian invitation is being requested. The identifier can be one of the following: * the numeric identifier for the user * the email address of the user * the string literal "me", indicating the requesting user * the string literal "-", indicating that results should be returned for all students that the requesting user is permitted to view guardian invitations. [Default] * **This is only allowed when excluding the `InvitationId` parameter to perform a List request!** .PARAMETER GuardianEmail If specified, only results with the specified GuardianEmail will be returned. .PARAMETER State If specified, only results with the specified state values will be returned. Otherwise, results with a state of PENDING will be returned. The State can be one of the following: * PENDING * COMPLETE .PARAMETER User The user to authenticate the request as .EXAMPLE Get-GSStudentGuardianInvitation -StudentId aristotle@athens.edu Gets the list of guardian invitations for this student. #> [cmdletbinding(DefaultParameterSetName = "List")] Param ( [parameter(Mandatory = $true,ParameterSetName = "Get")] [Alias('Id')] [String[]] $InvitationId, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias('Student')] [String[]] $StudentId = "-", [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true,ParameterSetName = "List")] [Alias('Guardian')] [String] $GuardianEmail, [parameter(Mandatory = $false,ParameterSetName = "List")] [Google.Apis.Classroom.v1.UserProfilesResource+GuardianInvitationsResource+ListRequest+StatesEnum[]] $States, [parameter(Mandatory = $false)] [String] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/classroom.guardianlinks.students' ServiceType = 'Google.Apis.Classroom.v1.ClassroomService' User = $User } $service = New-GoogleService @serviceParams } Process { foreach ($stuId in $StudentId) { try { if ($stuId -ne '-') { if ( -not ($stuId -as [decimal])) { if ($stuId -ceq 'me') { $stuId = $Script:PSGSuite.AdminEmail } elseif ($stuId -notlike "*@*.*") { $stuId = "$($stuId)@$($Script:PSGSuite.Domain)" } } } elseif ($PSCmdlet.ParameterSetName -eq 'Get') { Write-Error "You must specify a valid StudentId when using InvitationId! Current value '$stuId'" } switch ($PSCmdlet.ParameterSetName) { Get { foreach ($invId in $InvitationId) { try { Write-Verbose "Getting Guardian Invitation '$invId' for Student '$stuId'" $request = $service.UserProfiles.GuardianInvitations.Get($stuId,$invId) $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } List { try { if ($stuId -eq '-') { Write-Verbose "Listing all Guardian Invitations" } else { Write-Verbose "Listing Guardian Invitations for Student '$stuId'" } $request = $service.UserProfiles.GuardianInvitations.List($stuId) foreach ($s in $States) { $request.States += $s } if ($PSBoundParameters.Keys -contains 'GuardianEmail') { $request.InvitedEmailAddress = $GuardianEmail } [int]$i = 1 do { $result = $request.Execute() if ($null -ne $result.GuardianInvitations) { $result.GuardianInvitations } $request.PageToken = $result.NextPageToken [int]$retrieved = ($i + $result.GuardianInvitations.Count) - 1 Write-Verbose "Retrieved $retrieved Guardian Invitations..." [int]$i = $i + $result.GuardianInvitations.Count } until (!$result.NextPageToken) } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Get-GSStudentGuardianInvitation' function New-GSCourse { <# .SYNOPSIS Creates a course. .DESCRIPTION Creates a course. .PARAMETER Name Name of the course. For example, "10th Grade Biology". The name is required. It must be between 1 and 750 characters and a valid UTF-8 string. .PARAMETER OwnerId The identifier of the owner of a course. When specified as a parameter of a create course request, this field is required. The identifier can be one of the following: * the numeric identifier for the user * the email address of the user * the string literal "me", indicating the requesting user .PARAMETER Id Identifier for this course assigned by Classroom. When creating a course, you may optionally set this identifier to an alias string in the request to create a corresponding alias. The id is still assigned by Classroom and cannot be updated after the course is created. .PARAMETER Section Section of the course. For example, "Period 2". If set, this field must be a valid UTF-8 string and no longer than 2800 characters. .PARAMETER DescriptionHeading Optional heading for the description. For example, "Welcome to 10th Grade Biology." If set, this field must be a valid UTF-8 string and no longer than 3600 characters. .PARAMETER Description Optional description. For example, "We'll be learning about the structure of living creatures from a combination of textbooks, guest lectures, and lab work. Expect to be excited!" If set, this field must be a valid UTF-8 string and no longer than 30,000 characters. .PARAMETER Room Optional room location. For example, "301". If set, this field must be a valid UTF-8 string and no longer than 650 characters. .PARAMETER CourseState State of the course. If unspecified, the default state is PROVISIONED Available values are: * ACTIVE - The course is active. * ARCHIVED - The course has been archived. You cannot modify it except to change it to a different state. * PROVISIONED - The course has been created, but not yet activated. It is accessible by the primary teacher and domain administrators, who may modify it or change it to the ACTIVE or DECLINED states. A course may only be changed to PROVISIONED if it is in the DECLINED state. * DECLINED - The course has been created, but declined. It is accessible by the course owner and domain administrators, though it will not be displayed in the web UI. You cannot modify the course except to change it to the PROVISIONED state. A course may only be changed to DECLINED if it is in the PROVISIONED state. .PARAMETER User The user to authenticate the request as .EXAMPLE New-GSCourse -Name "The Rebublic" -OwnerId plato@athens.edu -Id the-republic-s01 -Section s01 -DescriptionHeading "The definition of justice, the order and character of the just city-state and the just man" -Room academy-01 #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0)] [ValidateLength(1,750)] [String] $Name, [parameter(Mandatory = $false)] [Alias('Teacher')] [String] $OwnerId, [parameter(Mandatory = $false)] [Alias('Alias')] [String] $Id, [parameter(Mandatory = $false)] [ValidateLength(1,2800)] [String] $Section, [parameter(Mandatory = $false)] [ValidateLength(1,3600)] [Alias('Heading')] [String] $DescriptionHeading, [parameter(Mandatory = $false)] [ValidateLength(1,30000)] [String] $Description, [parameter(Mandatory = $false)] [String] $Room, [parameter(Mandatory = $false)] [Alias('Status')] [ValidateSet('PROVISIONED','ACTIVE','ARCHIVED','DECLINED')] [String] $CourseState, [parameter(Mandatory = $false)] [String] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/classroom.courses' ServiceType = 'Google.Apis.Classroom.v1.ClassroomService' User = $User } $service = New-GoogleService @serviceParams } Process { try { Write-Verbose "Creating new Course '$Name'" $body = New-Object 'Google.Apis.Classroom.v1.Data.Course' foreach ($prop in $PSBoundParameters.Keys | Where-Object {$body.PSObject.Properties.Name -contains $_}) { switch ($prop) { OwnerId { try { [decimal]$PSBoundParameters[$prop] | Out-Null } catch { if ($PSBoundParameters[$prop] -ceq 'me') { $PSBoundParameters[$prop] = $Script:PSGSuite.AdminEmail } elseif ($PSBoundParameters[$prop] -notlike "*@*.*") { $PSBoundParameters[$prop] = "$($PSBoundParameters[$prop])@$($Script:PSGSuite.Domain)" } } $body.$prop = $PSBoundParameters[$prop] } Default { $body.$prop = $PSBoundParameters[$prop] } } } if ($PSBoundParameters.Keys -notcontains 'OwnerId') { $body.OwnerId = $Script:PSGSuite.AdminEmail } $request = $service.Courses.Create($body) $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'New-GSCourse' function New-GSCourseAlias { <# .SYNOPSIS Creates a course alias. .DESCRIPTION Creates a course alias. .PARAMETER Alias Alias string .PARAMETER CourseId Identifier of the course to alias. This identifier can be either the Classroom-assigned identifier or an alias. .PARAMETER Scope An alias uniquely identifies a course. It must be unique within one of the following scopes: * Domain - A domain-scoped alias is visible to all users within the alias creator's domain and can be created only by a domain admin. A domain-scoped alias is often used when a course has an identifier external to Classroom. * Project - A project-scoped alias is visible to any request from an application using the Developer Console project ID that created the alias and can be created by any project. A project-scoped alias is often used when an application has alternative identifiers. A random value can also be used to avoid duplicate courses in the event of transmission failures, as retrying a request will return ALREADY_EXISTS if a previous one has succeeded. .PARAMETER User The user to authenticate the request as .EXAMPLE New-GSCourseAlias -Alias "abc123" -CourseId 'architecture-101' -Scope Domain .EXAMPLE New-GSCourseAlias -Alias "d:abc123" -CourseId 'architecture-101' #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0)] [String] $Alias, [parameter(Mandatory = $true,Position = 1)] [String] $CourseId, [parameter(Mandatory = $false)] [ValidateSet('Domain','Project')] [String] $Scope = $(if($Alias -match "^p\:"){'Project'}else{'Domain'}), [parameter(Mandatory = $false)] [String] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/classroom.courses' ServiceType = 'Google.Apis.Classroom.v1.ClassroomService' User = $User } $service = New-GoogleService @serviceParams $formatted = if ($Alias -match "^(d\:|p\:)") { $Alias $Scope = if ($Alias -match "^d\:") { 'Domain' } else { 'Project' } } else { switch ($Scope) { Domain { 'd:' + ($Alias -replace "^(d\:|p\:)","") } Project { 'p:' + ($Alias -replace "^(d\:|p\:)","") } } } } Process { try { Write-Verbose "Creating new Alias '$Alias' for Course '$CourseId' at '$Scope' scope" $body = New-Object 'Google.Apis.Classroom.v1.Data.CourseAlias' -Property @{ Alias = $formatted } $request = $service.Courses.Aliases.Create($body,$CourseId) $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'New-GSCourseAlias' function New-GSCourseInvitation { <# .SYNOPSIS Creates a course invitation. .DESCRIPTION Creates a course invitation. .PARAMETER CourseId Identifier of the course to invite the user to. This identifier can be either the Classroom-assigned identifier or an alias. .PARAMETER UserId Identifier of the user to invite. The identifier can be one of the following: * the numeric identifier for the user * the email address of the user * the string literal "me", indicating the requesting user .PARAMETER Role Role to invite the user to have from the following: * STUDENT * TEACHER * OWNER .PARAMETER User The user to authenticate the request as .EXAMPLE New-GSCourseInvitation -CourseId philosophy-101 -UserId aristotle@athens.edu -Role TEACHER #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)] [String] $CourseId, [parameter(Mandatory = $false,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias('PrimaryEmail','Email','Mail')] [String[]] $UserId, [parameter(Mandatory = $false)] [ValidateSet('STUDENT','TEACHER','OWNER')] [String] $Role = 'STUDENT', [parameter(Mandatory = $false)] [String] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/classroom.rosters' ServiceType = 'Google.Apis.Classroom.v1.ClassroomService' User = $User } $service = New-GoogleService @serviceParams } Process { foreach ($U in $UserId) { try { if ( -not ($U -as [decimal])) { if ($U -ceq 'me') { $U = $Script:PSGSuite.AdminEmail } elseif ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } } Write-Verbose "Inviting User '$U' to Course '$CourseId' for Role '$Role'" $body = New-Object 'Google.Apis.Classroom.v1.Data.Invitation' foreach ($prop in $PSBoundParameters.Keys | Where-Object {$body.PSObject.Properties.Name -contains $_}) { $body.$prop = $PSBoundParameters[$prop] } if ($PSBoundParameters.Keys -notcontains 'Role') { $body.Role = $Role } $request = $service.Invitations.Create($body) $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'New-GSCourseInvitation' function New-GSStudentGuardianInvitation { <# .SYNOPSIS Creates a guardian invitation, and sends an email to the guardian asking them to confirm that they are the student's guardian. .DESCRIPTION Creates a guardian invitation, and sends an email to the guardian asking them to confirm that they are the student's guardian. .PARAMETER StudentId Identifier of the user to invite. The identifier can be one of the following: * the numeric identifier for the user * the email address of the user .PARAMETER GuardianEmail The email address of the guardian to invite. .PARAMETER User The user to authenticate the request as .EXAMPLE New-GSStudentGuardianInvitation -StudentId aristotle@athens.edu -GuardianEmail zeus@olympus.io .EXAMPLE Import-Csv .\Student_Guardian_List.csv | New-GSStudentGuardianInvitation Process a CSV with two columns containing headers "Student" and "Guardian" and send the invites accordingly, i.e. | StudentId | GuardianEmail | |:--------------------:|:---------------:| | aristotle@athens.edu | zeus@olympus.io | | plato@athens.edu | hera@olympus.io | #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)] [Alias('Student')] [String] $StudentId, [parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)] [Alias('Guardian')] [String] $GuardianEmail, [parameter(Mandatory = $false)] [String] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/classroom.guardianlinks.students' ServiceType = 'Google.Apis.Classroom.v1.ClassroomService' User = $User } $service = New-GoogleService @serviceParams } Process { try { if ( -not ($StudentId -as [decimal])) { if ($StudentId -ceq 'me') { $StudentId = $Script:PSGSuite.AdminEmail } elseif ($StudentId -notlike "*@*.*") { $StudentId = "$($StudentId)@$($Script:PSGSuite.Domain)" } } $body = New-Object 'Google.Apis.Classroom.v1.Data.GuardianInvitation' -Property @{ StudentId = $StudentId InvitedEmailAddress = $GuardianEmail } Write-Verbose "Inviting Guardian '$GuardianEmail' for Student '$StudentId'" $request = $service.UserProfiles.GuardianInvitations.Create($body,$StudentId) $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'New-GSStudentGuardianInvitation' function Remove-GSCourse { <# .SYNOPSIS Removes an existing course. .DESCRIPTION Removes an existing course. .PARAMETER Id Identifier for this course assigned by Classroom. .PARAMETER User The user to authenticate the request as .EXAMPLE Remove-GSCourse -Id the-republic-s01 -Confirm:$false #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $true,Position = 0)] [Alias('Alias')] [String] $Id, [parameter(Mandatory = $false)] [String] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/classroom.courses' ServiceType = 'Google.Apis.Classroom.v1.ClassroomService' User = $User } $service = New-GoogleService @serviceParams } Process { try { if ($PSCmdlet.ShouldProcess("Removing Course '$Id'")) { Write-Verbose "Removing Course '$Id'" $request = $service.Courses.Delete($Id) $request.Execute() Write-Verbose "Course '$Id' has been successfully removed" } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Remove-GSCourse' function Remove-GSCourseAlias { <# .SYNOPSIS Removes a course alias. .DESCRIPTION Removes a course alias. .PARAMETER Alias Alias string .PARAMETER CourseId Identifier of the course to alias. This identifier can be either the Classroom-assigned identifier or an alias. .PARAMETER User The user to authenticate the request as .EXAMPLE Remove-GSCourseAlias -Alias "d:abc123" -CourseId 'architecture-101' #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $true,Position = 0)] [String] $Alias, [parameter(Mandatory = $true,Position = 1)] [String] $CourseId, [parameter(Mandatory = $false)] [String] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/classroom.courses' ServiceType = 'Google.Apis.Classroom.v1.ClassroomService' User = $User } $service = New-GoogleService @serviceParams } Process { try { if ($PSCmdlet.ShouldProcess("Removing Alias '$Alias' for Course '$CourseId'")) { Write-Verbose "Removing Alias '$Alias' for Course '$CourseId'" $request = $service.Courses.Aliases.Delete($CourseId,$Alias) $request.Execute() Write-Verbose "Alias '$Alias' for Course '$CourseId' has been successfully removed" } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Remove-GSCourseAlias' function Remove-GSCourseInvitation { <# .SYNOPSIS Deletes an invitation. .DESCRIPTION Deletes an invitation. .PARAMETER Id Identifier of the invitation to delete. .PARAMETER User The user to authenticate the request as .EXAMPLE Remove-GSCourseInvitation -Id $inviteId -Confirm:$false #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $true,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [String[]] $Id, [parameter(Mandatory = $false)] [String] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/classroom.rosters' ServiceType = 'Google.Apis.Classroom.v1.ClassroomService' User = $User } $service = New-GoogleService @serviceParams } Process { foreach ($I in $Id) { try { if ($PSCmdlet.ShouldProcess("Removing Invitation '$I'")) { Write-Verbose "Removing Invitation '$I'" $request = $service.Invitations.Delete($I) $request.Execute() Write-Verbose "Invitation '$I' has been successfully removed" } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Remove-GSCourseInvitation' function Remove-GSCourseParticipant { <# .SYNOPSIS Removes students and/or teachers from a course .DESCRIPTION Removes students and/or teachers from a course .PARAMETER CourseId Identifier of the course to remove participants from. This identifier can be either the Classroom-assigned identifier or an alias. .PARAMETER Student Identifier of the user. This identifier can be one of the following: * the numeric identifier for the user * the email address of the user * the string literal "me", indicating the requesting user .PARAMETER Teacher Identifier of the user. This identifier can be one of the following: * the numeric identifier for the user * the email address of the user * the string literal "me", indicating the requesting user .PARAMETER User The user to authenticate the request as .EXAMPLE Remove-GSCourseParticipant -CourseId 'architecture-101' -Student plato@athens.edu,aristotle@athens.edu -Teacher zeus@athens.edu #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $true,Position = 0)] [String] $CourseId, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias('PrimaryEmail','Email','Mail')] [String[]] $Student, [parameter(Mandatory = $false)] [String[]] $Teacher, [parameter(Mandatory = $false)] [String] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/classroom.rosters' ServiceType = 'Google.Apis.Classroom.v1.ClassroomService' User = $User } $service = New-GoogleService @serviceParams } Process { foreach ($part in $Student) { try { if ( -not ($part -as [decimal])) { if ($part -ceq 'me') { $part = $Script:PSGSuite.AdminEmail } elseif ($part -notlike "*@*.*") { $part = "$($part)@$($Script:PSGSuite.Domain)" } } if ($PSCmdlet.ShouldProcess("Removing Student '$part' from Course '$CourseId'")) { Write-Verbose "Removing Student '$part' from Course '$CourseId'" $request = $service.Courses.Students.Delete($CourseId,$part) $request.Execute() Write-Verbose "Student '$part' has successfully been removed from Course '$CourseId'" } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } foreach ($part in $Teacher) { try { try { [decimal]$part | Out-Null } catch { if ($part -ceq 'me') { $part = $Script:PSGSuite.AdminEmail } elseif ($part -notlike "*@*.*") { $part = "$($part)@$($Script:PSGSuite.Domain)" } } if ($PSCmdlet.ShouldProcess("Removing Teacher '$part' from Course '$CourseId'")) { Write-Verbose "Removing Teacher '$part' from Course '$CourseId'" $request = $service.Courses.Teachers.Delete($CourseId,$part) $request.Execute() Write-Verbose "Teacher '$part' has successfully been removed from Course '$CourseId'" } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Remove-GSCourseParticipant' function Remove-GSStudentGuardian { <# .SYNOPSIS Removes a guardian. .DESCRIPTION Removes a guardian. .PARAMETER StudentId The identifier of the student to get guardian info for. The identifier can be one of the following: * the numeric identifier for the user * the email address of the user * the string literal "me", indicating the requesting user .PARAMETER GuardianId The id field from a Guardian. .PARAMETER User The user to authenticate the request as .EXAMPLE Remove-GSStudentGuardian -StudentId aristotle@athens.edu -GuardianId $guardianId Removes the guardian for artistotle@athens.edu. #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)] [Alias('Student')] [String] $StudentId, [parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)] [Alias('Guardian')] [String] $GuardianId, [parameter(Mandatory = $false)] [String] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/classroom.guardianlinks.students' ServiceType = 'Google.Apis.Classroom.v1.ClassroomService' User = $User } $service = New-GoogleService @serviceParams } Process { try { if ( -not ($StudentId -as [decimal])) { if ($StudentId -ceq 'me') { $StudentId = $Script:PSGSuite.AdminEmail } elseif ($StudentId -notlike "*@*.*") { $StudentId = "$($StudentId)@$($Script:PSGSuite.Domain)" } } if ($PSCmdlet.ShouldProcess("Removing Guardian '$GuardianId' from Student '$StudentId'")) { Write-Verbose "Removing Guardian '$GuardianId' from Student '$StudentId'" $request = $service.UserProfiles.Guardians.Delete($StudentId,$GuardianId) $request.Execute() } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Remove-GSStudentGuardian' function Update-GSCourse { <# .SYNOPSIS Updates an existing course. .DESCRIPTION Updates an existing course. .PARAMETER Id Identifier for this course assigned by Classroom. .PARAMETER Name Name of the course. For example, "10th Grade Biology". The name is required. It must be between 1 and 750 characters and a valid UTF-8 string. .PARAMETER OwnerId The identifier of the owner of a course. When specified as a parameter of a create course request, this field is required. The identifier can be one of the following: * the numeric identifier for the user * the email address of the user * the string literal "me", indicating the requesting user .PARAMETER Section Section of the course. For example, "Period 2". If set, this field must be a valid UTF-8 string and no longer than 2800 characters. .PARAMETER DescriptionHeading Optional heading for the description. For example, "Welcome to 10th Grade Biology." If set, this field must be a valid UTF-8 string and no longer than 3600 characters. .PARAMETER Description Optional description. For example, "We'll be learning about the structure of living creatures from a combination of textbooks, guest lectures, and lab work. Expect to be excited!" If set, this field must be a valid UTF-8 string and no longer than 30,000 characters. .PARAMETER Room Optional room location. For example, "301". If set, this field must be a valid UTF-8 string and no longer than 650 characters. .PARAMETER CourseState State of the course. If unspecified, the default state is PROVISIONED Available values are: * ACTIVE - The course is active. * ARCHIVED - The course has been archived. You cannot modify it except to change it to a different state. * PROVISIONED - The course has been created, but not yet activated. It is accessible by the primary teacher and domain administrators, who may modify it or change it to the ACTIVE or DECLINED states. A course may only be changed to PROVISIONED if it is in the DECLINED state. * DECLINED - The course has been created, but declined. It is accessible by the course owner and domain administrators, though it will not be displayed in the web UI. You cannot modify the course except to change it to the PROVISIONED state. A course may only be changed to DECLINED if it is in the PROVISIONED state. .PARAMETER User The user to authenticate the request as .EXAMPLE Update-GSCourse -Id the-republic-s01 -Name "The Rebublic 101" #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0)] [Alias('Alias')] [String] $Id, [parameter(Mandatory = $false)] [ValidateLength(1,750)] [String] $Name, [parameter(Mandatory = $false)] [Alias('Teacher')] [String] $OwnerId, [parameter(Mandatory = $false)] [ValidateLength(1,2800)] [String] $Section, [parameter(Mandatory = $false)] [ValidateLength(1,3600)] [Alias('Heading')] [String] $DescriptionHeading, [parameter(Mandatory = $false)] [ValidateLength(1,30000)] [String] $Description, [parameter(Mandatory = $false)] [String] $Room, [parameter(Mandatory = $false)] [Alias('Status')] [ValidateSet('PROVISIONED','ACTIVE','ARCHIVED','DECLINED')] [String] $CourseState, [parameter(Mandatory = $false)] [String] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/classroom.courses' ServiceType = 'Google.Apis.Classroom.v1.ClassroomService' User = $User } $service = New-GoogleService @serviceParams $UpdateMask = @() } Process { try { Write-Verbose "Updating Course ID '$Id'" $body = New-Object 'Google.Apis.Classroom.v1.Data.Course' foreach ($prop in $PSBoundParameters.Keys | Where-Object {$body.PSObject.Properties.Name -contains $_}) { switch ($prop) { Id {} OwnerId { $UpdateMask += $($prop.Substring(0,1).ToLower() + $prop.Substring(1)) try { [decimal]$PSBoundParameters[$prop] | Out-Null } catch { if ($PSBoundParameters[$prop] -ceq 'me') { $PSBoundParameters[$prop] = $Script:PSGSuite.AdminEmail } elseif ($PSBoundParameters[$prop] -notlike "*@*.*") { $PSBoundParameters[$prop] = "$($PSBoundParameters[$prop])@$($Script:PSGSuite.Domain)" } } $body.$prop = $PSBoundParameters[$prop] } Default { $UpdateMask += $($prop.Substring(0,1).ToLower() + $prop.Substring(1)) $body.$prop = $PSBoundParameters[$prop] } } } $request = $service.Courses.Patch($body,$Id) $request.UpdateMask = $($UpdateMask -join ",") $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Update-GSCourse' function Get-GSChatConfig { <# .SYNOPSIS Returns the specified Chat space and webhook dictionaries from the PSGSuite config to use with Send-GSChatMessage .DESCRIPTION Returns the specified Chat space and webhook dictionaries from the PSGSuite config to use with Send-GSChatMessage .PARAMETER WebhookName The key that the Webhook Url is stored as in the Config. If left blank, returns the full Chat configuration from the Config .PARAMETER SpaceName The key that the Space ID is stored as in the Config. If left blank, returns the full Chat configuration from the Config .PARAMETER ConfigName The name of the Config to return the Chat config items from .EXAMPLE Send-GSChatMessage -Text "Testing webhook" -Webhook (Get-GSChatConfig MyRoom) Sends a Chat message with text to the Webhook Url named 'MyRoom' found in the config #> [CmdletBinding(DefaultParameterSetName = "Webhooks")] Param ( [parameter(Mandatory = $false,Position = 0,ParameterSetName = "Webhooks")] [String[]] $WebhookName, [parameter(Mandatory = $false,Position = 0,ParameterSetName = "Spaces")] [String[]] $SpaceName, [parameter(Mandatory = $false,Position = 1)] [String[]] $ConfigName ) if ($PSBoundParameters.Keys -contains 'ConfigName') { $currentConfig = Get-PSGSuiteConfig -ConfigName $ConfigName -PassThru -NoImport } else { $currentConfig = Get-PSGSuiteConfig -PassThru } switch ($PSCmdlet.ParameterSetName) { Webhooks { if ($PSBoundParameters.Keys -contains 'WebhookName') { foreach ($hook in $WebhookName) { Write-Verbose "Getting webhook for '$hook' from ConfigName '$($currentConfig.ConfigName)'" if ($found = $currentConfig.Chat['Webhooks'][$hook]) { $found } else { Write-Error "$hook was not found in the Webhook dictionary stored in ConfigName '$($currentConfig.ConfigName)'!" } } } else { Write-Verbose "Getting full Chat config from ConfigName '$($currentConfig.ConfigName)'" $currentConfig.Chat } } Spaces { foreach ($hook in $SpaceName) { Write-Verbose "Getting space Id for '$hook' from ConfigName '$($currentConfig.ConfigName)'" if ($found = $currentConfig.Chat['Spaces'][$hook]) { $found } else { Write-Error "$hook was not found in the Spaces dictionary stored in ConfigName '$($currentConfig.ConfigName)'!" } } } } } Export-ModuleMember -Function 'Get-GSChatConfig' function Get-PSGSuiteConfig { <# .SYNOPSIS Loads the specified PSGSuite config .DESCRIPTION Loads the specified PSGSuite config .PARAMETER ConfigName The config name to load .PARAMETER Path The path of the config to load if non-default. This can be used to load either a legacy XML config from an older version of PSGSuite or a specific .PSD1 config created with version 2.0.0 or greater .PARAMETER Scope The config scope to load .PARAMETER PassThru If specified, returns the config after loading it .EXAMPLE Get-PSGSuiteConfig personalDomain -PassThru This will load the config named "personalDomain" and return it as a PSObject. #> [cmdletbinding(DefaultParameterSetName = "ConfigurationModule")] Param ( [parameter(Mandatory = $false,Position = 0,ParameterSetName = "ConfigurationModule")] [String] $ConfigName, [Parameter(Mandatory = $false,ParameterSetName = "Path")] [ValidateScript( {Test-Path $_})] [String] $Path, [Parameter(Mandatory = $false,Position = 1)] [ValidateSet("User", "Machine", "Enterprise", $null)] [string] $Scope = $Script:ConfigScope, [Parameter(Mandatory = $false)] [Switch] $PassThru, [Parameter(Mandatory = $false)] [Switch] $NoImport ) function Decrypt { param($String) if ($String -is [System.Security.SecureString]) { [System.Runtime.InteropServices.Marshal]::PtrToStringAuto( [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR( $string)) } elseif ($String -is [System.String]) { $String } } $script:ConfigScope = $Scope switch ($PSCmdlet.ParameterSetName) { ConfigurationModule { $fullConf = Import-SpecificConfiguration -CompanyName 'SCRT HQ' -Name 'PSGSuite' -Scope $Script:ConfigScope if (!$ConfigName) { $choice = $fullConf["DefaultConfig"] Write-Verbose "Importing default config: $choice" } else { $choice = $ConfigName Write-Verbose "Importing config: $choice" } $encConf = [PSCustomObject]($fullConf[$choice]) } Path { $encConf = switch ((Get-Item -Path $Path).Extension) { '.xml' { Import-Clixml -Path $Path $choice = "LegacyXML" } '.psd1' { Import-SpecificConfiguration -Path $Path $choice = "CustomConfigurationFile" } } } } $decryptedConfig = $encConf | Select-Object -Property @{l = 'ConfigName';e = {$choice}}, @{l = 'P12KeyPath';e = {Decrypt $_.P12KeyPath}}, @{l = 'ClientSecretsPath';e = {Decrypt $_.ClientSecretsPath}}, @{l = 'ClientSecrets';e = {Decrypt $_.ClientSecrets}}, @{l = 'AppEmail';e = {Decrypt $_.AppEmail}}, @{l = 'AdminEmail';e = {Decrypt $_.AdminEmail}}, @{l = 'CustomerID';e = {Decrypt $_.CustomerID}}, @{l = 'Domain';e = {Decrypt $_.Domain}}, @{l = 'Preference';e = {Decrypt $_.Preference}}, @{l = 'ServiceAccountClientID';e = {Decrypt $_.ServiceAccountClientID}}, @{l = 'Chat';e = { $dict = @{ Webhooks = @{} Spaces = @{} } foreach ($key in $_.Chat.Webhooks.Keys) { $dict['Webhooks'][$key] = (Decrypt $_.Chat.Webhooks[$key]) } foreach ($key in $_.Chat.Spaces.Keys) { $dict['Spaces'][$key] = (Decrypt $_.Chat.Spaces[$key]) } $dict }}, @{l = 'ConfigPath';e = {if ($_.ConfigPath) { $_.ConfigPath } elseif ($Path) { "$(Resolve-Path $Path)" } else { $null } } } Write-Verbose "Retrieved configuration '$choice'" if (!$NoImport) { $script:PSGSuite = $decryptedConfig } if ($PassThru) { $decryptedConfig } } Export-ModuleMember -Function 'Get-PSGSuiteConfig' function Set-PSGSuiteConfig { <# .SYNOPSIS Creates or updates a config .DESCRIPTION Creates or updates a config .PARAMETER ConfigName The friendly name for the config you are creating or updating .PARAMETER P12KeyPath The path to the P12 Key file downloaded from the Google Developer's Console. If both P12KeyPath and ClientSecretsPath are specified, P12KeyPath takes precedence .PARAMETER ClientSecretsPath The path to the Client Secrets JSON file downloaded from the Google Developer's Console. Using the ClientSecrets JSON will prompt the user to complete OAuth2 authentication in their browser on the first run and store the retrieved Refresh and Access tokens in the user's home directory. If P12KeyPath is also specified, ClientSecretsPath will be ignored. .PARAMETER ClientSecrets The string contents of the Client Secrets JSON file downloaded from the Google Developer's Console. Using the ClientSecrets JSON will prompt the user to complete OAuth2 authentication in their browser on the first run and store the retrieved Refresh and Access tokens in the user's home directory. If P12KeyPath is also specified, ClientSecrets will be ignored. .PARAMETER AppEmail The application email from the Google Developer's Console. This typically looks like the following: myProjectName@myProject.iam.gserviceaccount.com .PARAMETER AdminEmail The email of the Google Admin running the functions. This will typically be your email. .PARAMETER CustomerID The Customer ID for your customer. If unknown, you can retrieve it by running Get-GSUser after creating a base config with at least either the P12KeyPath or ClientSecretsPath, the AppEmail and the AdminEmail. .PARAMETER Domain The domain that you primarily manage for this CustomerID .PARAMETER Preference Some functions allow you to specify whether you are running in the context of the customer or a specific domain in the customer's realm. This allows you to set your preference. Available values are: * CustomerID * Domain .PARAMETER ServiceAccountClientID The Service Account's Client ID from the Google Developer's Console. This is optional and is only used as a reference for yourself to prevent needing to check the Developer's Console for the ID when verifying API Client Access. .PARAMETER Webhook Web .PARAMETER Scope The scope at which you would like to set this config. Available values are: * Machine (this would create the config in a location accessible by all users on the machine) * Enterprise (this would create the config in the Roaming AppData folder for the user or it's *nix equivalent) * User (this would create the config in the Local AppData folder for the user or it's *nix equivalent) .PARAMETER SetAsDefaultConfig If passed, sets the ConfigName as the default config to load on module import .PARAMETER NoImport The default behavior when using Set-PSGSuiteConfig is that the new/updated config is imported as active. If -NoImport is passed, this saves the config but retains the previously loaded config as active. .EXAMPLE Set-PSGSuiteConfig -ConfigName "personal" -P12KeyPath C:\Keys\PersonalKey.p12 -AppEmail "myProjectName@myProject.iam.gserviceaccount.com" -AdminEmail "admin@domain.com" -CustomerID "C83030001" -Domain "domain.com" -Preference CustomerID -ServiceAccountClientID 1175798883298324983498 -SetAsDefaultConfig This builds a config names "personal" and sets it as the default config #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingConvertToSecureStringWithPlainText", "")] [cmdletbinding()] Param ( [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [ValidateScript( { if ($_ -eq "DefaultConfig") { throw "You must specify a ConfigName other than 'DefaultConfig'. That is a reserved value." } elseif ($_ -notmatch '^[a-zA-Z]+[a-zA-Z0-9]*$') { throw "You must specify a ConfigName that starts with a letter and does not contain any spaces, otherwise the Configuration will break" } else { $true } })] [string] $ConfigName = $Script:ConfigName, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [string] $P12KeyPath, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [string] $ClientSecretsPath, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [string] $ClientSecrets, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [string] $AppEmail, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [string] $AdminEmail, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [string] $CustomerID, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [string] $Domain, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [ValidateSet("CustomerID","Domain")] [string] $Preference, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [string] $ServiceAccountClientID, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Hashtable[]] $Webhook, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Hashtable[]] $Space, [parameter(Mandatory = $false)] [ValidateSet("User", "Machine", "Enterprise", $null)] [string] $Scope = $script:ConfigScope, [parameter(Mandatory = $false)] [switch] $SetAsDefaultConfig, [parameter(Mandatory = $false)] [switch] $NoImport ) Begin { Function Encrypt { param($string) if ($string -is [System.Security.SecureString]) { $string } elseif ($string -is [System.String] -and $String -notlike '') { ConvertTo-SecureString -String $string -AsPlainText -Force } } } Process { $script:ConfigScope = $Scope $params = @{} if ($PSBoundParameters.Keys -contains "Verbose") { $params["Verbose"] = $PSBoundParameters["Verbose"] } $configHash = Import-SpecificConfiguration -CompanyName 'SCRT HQ' -Name 'PSGSuite' @params if (!$ConfigName) { $ConfigName = if ($configHash["DefaultConfig"]){ $configHash["DefaultConfig"] } else { "default" $configHash["DefaultConfig"] = "default" } } Write-Verbose "Setting config name '$ConfigName'" $configParams = @('P12KeyPath','ClientSecretsPath','ClientSecrets','AppEmail','AdminEmail','CustomerID','Domain','Preference','ServiceAccountClientID','Webhook','Space') if ($SetAsDefaultConfig -or !$configHash["DefaultConfig"]) { $configHash["DefaultConfig"] = $ConfigName } if (!$configHash[$ConfigName]) { $configHash.Add($ConfigName,(@{})) } foreach ($key in ($PSBoundParameters.Keys | Where-Object {$configParams -contains $_})) { switch ($key) { ClientSecretsPath { $configHash["$ConfigName"][$key] = (Encrypt $PSBoundParameters[$key]) $configHash["$ConfigName"]['ClientSecrets'] = (Encrypt $(Get-Content $PSBoundParameters[$key] -Raw)) } Webhook { if ($configHash["$ConfigName"].Keys -notcontains 'Chat') { $configHash["$ConfigName"]['Chat'] = @{ Webhooks = @{} Spaces = @{} } } foreach ($cWebhook in $PSBoundParameters[$key]) { foreach ($cWebhookKey in $cWebhook.Keys) { $configHash["$ConfigName"]['Chat']['Webhooks'][$cWebhookKey] = (Encrypt $cWebhook[$cWebhookKey]) } } } Space { if ($configHash["$ConfigName"].Keys -notcontains 'Chat') { $configHash["$ConfigName"]['Chat'] = @{ Webhooks = @{} Spaces = @{} } } $configHash["$ConfigName"]['Chat']['Spaces'] = @{} foreach ($cWebhook in $PSBoundParameters[$key]) { foreach ($cWebhookKey in $cWebhook.Keys) { $configHash["$ConfigName"]['Chat']['Spaces'][$cWebhookKey] = (Encrypt $cWebhook[$cWebhookKey]) } } } default { $configHash["$ConfigName"][$key] = (Encrypt $PSBoundParameters[$key]) } } } $configHash["$ConfigName"]['ConfigPath'] = (Join-Path $(Get-Module PSGSuite | Get-StoragePath -Scope $Script:ConfigScope) "Configuration.psd1") $configHash | Export-Configuration -CompanyName 'SCRT HQ' -Name 'PSGSuite' -Scope $script:ConfigScope } End { if (!$NoImport) { Get-PSGSuiteConfig -ConfigName $ConfigName -Verbose:$false } } } Export-ModuleMember -Function 'Set-PSGSuiteConfig' function Show-PSGSuiteConfig { <# .SYNOPSIS Returns the currently loaded config .DESCRIPTION Returns the currently loaded config .EXAMPLE Show-PSGSuiteConfig #> [CmdletBinding()] Param() Write-Verbose "Showing current PSGSuite config" $script:PSGSuite } Export-ModuleMember -Function 'Show-PSGSuiteConfig' function Switch-PSGSuiteConfig { <# .SYNOPSIS Switches the active config .DESCRIPTION Switches the active config .PARAMETER ConfigName The friendly name of the config you would like to set as active for the session .PARAMETER Domain The domain name for the config you would like to set as active for the session .PARAMETER SetToDefault If passed, also sets the specified config as the default so it's loaded on the next module import .EXAMPLE Switch-PSGSuiteConfig newCustomer Switches the config to the "newCustomer" config #> [CmdletBinding(DefaultParameterSetName = "ConfigName")] Param ( [parameter(Mandatory = $true,Position = 0,ParameterSetName = "ConfigName")] [ValidateNotNullOrEmpty()] [String] $ConfigName, [parameter(Mandatory = $true,Position = 0,ParameterSetName = "Domain")] [ValidateNotNullOrEmpty()] [String] $Domain, [parameter(Mandatory = $false)] [switch] $SetToDefault ) if ($script:PSGSuite.Domain -eq $Domain) { Write-Verbose "Current config is already set to domain '$Domain' --- retaining current config. If you would like to import a different config for the same domain, please use the -ConfigName parameter instead" if ($SetToDefault) { Write-Verbose "Setting config name '$($script:PSGSuite.ConfigName)' for domain '$($script:PSGSuite.Domain)' as default" Set-PSGSuiteConfig -ConfigName $($script:PSGSuite.ConfigName) -SetAsDefaultConfig -Verbose:$false } } elseif ($script:PSGSuite.ConfigName -eq $ConfigName) { Write-Verbose "Current config is already set to '$ConfigName' --- retaining current config" if ($SetToDefault) { Write-Verbose "Setting config name '$($script:PSGSuite.ConfigName)' for domain '$($script:PSGSuite.Domain)' as default" Set-PSGSuiteConfig -ConfigName $($script:PSGSuite.ConfigName) -SetAsDefaultConfig -Verbose:$false } } else { function Decrypt { param($String) if ($String -is [System.Security.SecureString]) { [System.Runtime.InteropServices.Marshal]::PtrToStringAuto( [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR( $string)) } elseif ($String -is [System.String]) { $String } } $fullConf = Import-SpecificConfiguration -CompanyName 'SCRT HQ' -Name 'PSGSuite' -Scope $Script:ConfigScope -Verbose:$false $defaultConfigName = $fullConf['DefaultConfig'] $choice = switch ($PSCmdlet.ParameterSetName) { Domain { Write-Verbose "Switching active domain to '$Domain'" $fullConf.Keys | Where-Object {(Decrypt $fullConf[$_]['Domain']) -eq $Domain} } ConfigName { Write-Verbose "Switching active config to '$ConfigName'" $fullConf.Keys | Where-Object {$_ -eq $ConfigName} } } if ($choice) { $script:PSGSuite = [PSCustomObject]($fullConf[$choice]) | Select-Object -Property @{l = 'ConfigName';e = {$choice}}, @{l = 'P12KeyPath';e = {Decrypt $_.P12KeyPath}}, @{l = 'ClientSecretsPath';e = {Decrypt $_.ClientSecretsPath}}, @{l = 'ClientSecrets';e = {Decrypt $_.ClientSecrets}}, @{l = 'AppEmail';e = {Decrypt $_.AppEmail}}, @{l = 'AdminEmail';e = {Decrypt $_.AdminEmail}}, @{l = 'CustomerID';e = {Decrypt $_.CustomerID}}, @{l = 'Domain';e = {Decrypt $_.Domain}}, @{l = 'Preference';e = {Decrypt $_.Preference}}, @{l = 'ServiceAccountClientID';e = {Decrypt $_.ServiceAccountClientID}}, @{l = 'Webhook';e = { $dict = @{} foreach ($key in $_.Webhook.Keys) { $dict[$key] = (Decrypt $_.Webhook[$key]) } $dict }}, ConfigPath if ($SetToDefault) { if ($defaultConfigName -ne $choice) { Write-Verbose "Setting config name '$choice' for domain '$($script:PSGSuite.Domain)' as default" Set-PSGSuiteConfig -ConfigName $choice -SetAsDefaultConfig -Verbose:$false $env:PSGSuiteDefaultDomain = $script:PSGSuite.Domain [Environment]::SetEnvironmentVariable("PSGSuiteDefaultDomain", $script:PSGSuite.Domain, "User") } else { Write-Warning "Config name '$choice' for domain '$($script:PSGSuite.Domain)' is already set to default --- no action taken" } } } else { switch ($PSCmdlet.ParameterSetName) { Domain { Write-Warning "No config found for domain '$Domain'! Retaining existing config for domain '$($script:PSGSuite.Domain)'" } ConfigName { Write-Warning "No config named '$ConfigName' found! Retaining existing config '$($script:PSGSuite.ConfigName)'" } } } } } Export-ModuleMember -Function 'Switch-PSGSuiteConfig' Function Get-GSContactList { <# .SYNOPSIS Gets all contacts for the specified user .DESCRIPTION Gets all contacts for the specified user .PARAMETER User The primary email or UserID of the user. You can exclude the '@domain.com' to insert the Domain in the config or use the special 'me' to indicate the AdminEmail in the config. Defaults to the AdminEmail in the config. .EXAMPLE Get-GSContactList -User user@domain.com #> [cmdletbinding()] Param ( [parameter(Mandatory = $false, Position = 0, ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail", "UserKey", "Mail")] [ValidateNotNullOrEmpty()] [string[]] $User = $Script:PSGSuite.AdminEmail ) Process { foreach ($U in $User) { try { if ($U -ceq 'me') { $U = $Script:PSGSuite.AdminEmail } elseif ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } $Token = Get-GSToken -Scopes 'https://www.google.com/m8/feeds' -AdminEmail $U $Uri = "https://www.google.com/m8/feeds/contacts/$($U)/full?max-results=5000" $headers = @{ Authorization = "Bearer $($Token)" 'GData-Version' = '3.0' } Write-Verbose "Getting all contacts for user '$U'" $Raw = @() do { $Response = Invoke-WebRequest -Method Get -Uri ([Uri]$Uri) -Headers $headers -ContentType 'application/xml' -Verbose:$false $Feed = [xml]$Response.Content $Raw += $feed.feed.entry $Uri = $Feed.Feed.Link | Where-Object {$_.rel -eq "next"} | Select-Object -ExpandProperty Href Write-Verbose "Retrieved $($Raw.Count) contacts..." } until (-not $Uri) If ($Raw) { ForEach ($i in $Raw) { [PSCustomObject]@{ User = $U Id = ($i.id.Split("/")[-1]) Title = $i.title FullName = $i.name.fullName GivenName = $i.name.givenName FamilyName = $i.name.familyName EmailAddresses = $(if($i.email.address){$i.email.address}else{$null}) PhoneNumber = $i.phonenumber Updated = $i.updated Edited = $i.edited.'#text' Path = $(if($i.email.rel){$i.email.rel}else{$null}) Etag = $i.etag FullObject = $i } } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Get-GSContactList' Function Remove-GSContact { <# .SYNOPSIS Removes the specified contact .DESCRIPTION Removes the specified contact .PARAMETER ContactID The ContactID to be removed. .PARAMETER User The primary email or UserID of the user. You can exclude the '@domain.com' to insert the Domain in the config or use the special 'me' to indicate the AdminEmail in the config. Defaults to the AdminEmail in the config. .PARAMETER Etag The Etag string from a Get-GSContactList object. Used to ensure that no changes have been made to the contact since it was viewed in order to prevent data loss. Defaults to special Etag value *, which can be used to bypass this verification and process the update regardless of updates from other clients. .EXAMPLE Recommended to use Get-GSContactList to find and pipe desired contacts to Remove-GSContact: Get-GSContactList -User user@domain.com | Where-Object {"@baddomain.com" -match $_.EmailAddresses} | Remove-GSContact Removes all contacts for user@domain.com that have an email address on the baddomain.com domain. #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High")] Param ( [parameter(Mandatory = $false, Position = 0, ValueFromPipelineByPropertyName = $true)] [Alias("Id")] [string[]] $ContactId, [parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail", "UserKey", "Mail")] [ValidateNotNullOrEmpty()] [string] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] [string] $Etag = '*' ) Process { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $Token = Get-GSToken -Scopes 'https://www.google.com/m8/feeds' -AdminEmail $User $headers = @{ Authorization = "Bearer $($Token)" 'GData-Version' = '3.0' 'If-Match' = $Etag } foreach ($Id in $ContactID) { if ($PSCmdlet.ShouldProcess("Removing contact ID '$Id' for $User")) { Write-Verbose "Removing contact ID '$Id' for $User" try { $Uri = "https://www.google.com/m8/feeds/contacts/$($User)/full/$($Id)" $Response = Invoke-WebRequest -Method "Delete" -Uri ([Uri]$Uri) -Headers $headers -Verbose:$false If ($Response.StatusCode -eq "200") { Write-Verbose "Successfully deleted contact ID '$Id' for $User" } Else { Write-Verbose "HTTP $($Response.StatusCode): $($Response.StatusDescription)" } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } } Export-ModuleMember -Function 'Remove-GSContact' function Get-GSDataTransferApplication { <# .SYNOPSIS Gets the list of available Data Transfer Applications and their parameters .DESCRIPTION Gets the list of available Data Transfer Applications and their parameters .PARAMETER ApplicationId The Application Id of the Data Transfer Application you would like to return info for specifically. Exclude to return the full list .PARAMETER PageSize PageSize of the result set. Defaults to 500 (although it's typically a much smaller number for most Customers) .EXAMPLE Get-GSDataTransferApplication Gets the list of available Data Transfer Applications #> [cmdletbinding()] Param ( [parameter(Mandatory = $false,Position = 0)] [String[]] $ApplicationId, [parameter(Mandatory = $false)] [ValidateRange(1,500)] [Int] $PageSize = 500 ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.datatransfer' ServiceType = 'Google.Apis.Admin.DataTransfer.datatransfer_v1.DataTransferService' } $service = New-GoogleService @serviceParams } Process { try { if ($ApplicationId) { foreach ($I in $ApplicationId) { $request = $service.Applications.Get($I) $request.Execute() } } else { $request = $service.Applications.List() $request.CustomerId = $Script:PSGSuite.CustomerID if ($PageSize) { $request.MaxResults = $PageSize } Write-Verbose "Getting all Data Transfer Applications" $response = @() [int]$i = 1 do { $result = $request.Execute() $response += $result.Applications if ($result.NextPageToken) { $request.PageToken = $result.NextPageToken } [int]$retrieved = ($i + $result.Applications.Count) - 1 Write-Verbose "Retrieved $retrieved Data Transfer Applications..." [int]$i = $i + $result.Applications.Count } until (!$result.NextPageToken) return $response } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSDataTransferApplication' function Start-GSDataTransfer { <# .SYNOPSIS Starts a Data Transfer from one user to another .DESCRIPTION Starts a Data Transfer from one user to another .PARAMETER OldOwnerUserId The email or unique Id of the owner you are transferring data *FROM* .PARAMETER NewOwnerUserId The email or unique Id of the owner you are transferring data *TO* .PARAMETER ApplicationId The application Id that you would like to transfer data for .PARAMETER PrivacyLevel The privacy level for the data you'd like to transfer Available values are: * "SHARED": all shared content owned by the user * "PRIVATE": all private (unshared) content owned by the user .EXAMPLE Start-GSDataTransfer -OldOwnerUserId joe -NewOwnerUserId mark -ApplicationId 55656082996 -PrivacyLevel SHARED,PRIVATE Transfers all of Joe's data to Mark #> [cmdletbinding()] Param ( [parameter(Mandatory=$true,Position=0)] [string] $OldOwnerUserId, [parameter(Mandatory=$true,Position=1)] [string] $NewOwnerUserId, [parameter(Mandatory=$true,Position=2,ValueFromPipelineByPropertyName=$true)] [alias("id")] [string] $ApplicationId, [parameter(Mandatory=$true)] [ValidateSet("SHARED","PRIVATE")] [string[]] $PrivacyLevel ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.datatransfer' ServiceType = 'Google.Apis.Admin.DataTransfer.datatransfer_v1.DataTransferService' } $service = New-GoogleService @serviceParams $OldOwnerUserId = try { [bigint]$OldOwnerUserId } catch { Write-Verbose "Resolving Old Owner's UserId from '$OldOwnerUserId'" [bigint](Get-GSUser -User $OldOwnerUserId -Projection Basic -Verbose:$false | Select-Object -ExpandProperty id) } $NewOwnerUserId = try { [bigint]$NewOwnerUserId } catch { Write-Verbose "Resolving New Owner's UserId from '$NewOwnerUserId'" [bigint](Get-GSUser -User $NewOwnerUserId -Projection Basic -Verbose:$false | Select-Object -ExpandProperty id) } } Process { try { $body = New-Object 'Google.Apis.Admin.DataTransfer.datatransfer_v1.Data.DataTransfer' -Property @{ OldOwnerUserId = $OldOwnerUserId NewOwnerUserId = $NewOwnerUserId } $AppDataTransfers = New-Object 'Google.Apis.Admin.DataTransfer.datatransfer_v1.Data.ApplicationDataTransfer' -Property @{ ApplicationId = $ApplicationId } if ($PrivacyLevel) { $AppDataTransfers.ApplicationTransferParams = [Google.Apis.Admin.DataTransfer.datatransfer_v1.Data.ApplicationTransferParam[]](New-Object 'Google.Apis.Admin.DataTransfer.datatransfer_v1.Data.ApplicationTransferParam' -Property @{ Key = 'PRIVACY_LEVEL' Value = [String[]]$PrivacyLevel }) } $body.ApplicationDataTransfers = [Google.Apis.Admin.DataTransfer.datatransfer_v1.Data.ApplicationDataTransfer[]]$AppDataTransfers $request = $service.Transfers.Insert($body) Write-Verbose "Starting Data Transfer from User Id '$OldOwnerUserId' to User Id '$NewOwnerUserId'" $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Start-GSDataTransfer' function Add-GSGmailDelegate { <# .SYNOPSIS Adds a delegate with its verification status set directly to accepted, without sending any verification email. The delegate user must be a member of the same G Suite organization as the delegator user. .DESCRIPTION Adds a delegate with its verification status set directly to accepted, without sending any verification email. The delegate user must be a member of the same G Suite organization as the delegator user. Gmail imposes limtations on the number of delegates and delegators each user in a G Suite organization can have. These limits depend on your organization, but in general each user can have up to 25 delegates and up to 10 delegators. Note that a delegate user must be referred to by their primary email address, and not an email alias. Also note that when a new delegate is created, there may be up to a one minute delay before the new delegate is available for use. .PARAMETER User User's email address to delegate access to. .PARAMETER Delegate Delegate's email address to receive delegate access. .EXAMPLE Add-GSGmailDelegate -User tony@domain.com -Delegate peter@domain.com Provide Peter delegate access to Tony's inbox. #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0)] [Alias("From","Delegator")] [ValidateNotNullOrEmpty()] [String] $User, [parameter(Mandatory = $true,Position = 1)] [Alias("To")] [ValidateNotNullOrEmpty()] [String] $Delegate ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } if ($Delegate -notlike "*@*.*") { $Delegate = "$($Delegate)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/gmail.settings.sharing' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $User } $service = New-GoogleService @serviceParams } Process { try { Write-Verbose "Adding delegate access to user '$User's inbox for delegate '$Delegate'" $body = New-Object 'Google.Apis.Gmail.v1.Data.Delegate' -Property @{ DelegateEmail = $Delegate } $request = $service.Users.Settings.Delegates.Create($body,$User) $request.Execute() } catch { $origError = $_ if ($group = Get-GSGroup -Group $User -Verbose:$false -ErrorAction SilentlyContinue) { Write-Warning "$User is a group email, not a user account. You can only manage delegate access for a user's inbox. Please add $Delegate to the group $User instead." } elseif ($group = Get-GSGroup -Group $Delegate -Verbose:$false -ErrorAction SilentlyContinue) { Write-Warning "$Delegate is a group email, not a user account. You can only delegate access to other users." } else { $dele = Get-GSGmailDelegates -User $User -NoGroupCheck -ErrorAction SilentlyContinue -Verbose:$false if ($dele.DelegateEmail -contains $Delegate -and $dele.VerificationStatus -eq 'accepted') { Write-Warning "'$Delegate' already has delegate access to user '$User's inbox. No action needed." } elseif ($dele.DelegateEmail -contains $Delegate -and $dele.VerificationStatus -ne 'accepted') { Write-Warning "$Delegate was already invited for delegated access to user '$User's inbox, but VerificationStatus is currently '$($dele.VerificationStatus)'" } else { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($origError) } else { Write-Error $origError } } } } } } Export-ModuleMember -Function 'Add-GSGmailDelegate' function Get-GSGmailDelegate { <# .SYNOPSIS Gets delegates for the specified account. .DESCRIPTION Gets delegates for the specified account. .PARAMETER User User's email to get delegates for. .PARAMETER Delegate The specific delegate to get. If excluded returns the list of delegates for the user. .PARAMETER NoGroupCheck By default, this will check if the User email is a group email which cannot be delegated if the attempt to delegate access fails. Include this switch to prevent the group check and return the original error. .EXAMPLE Get-GSGmailDelegate -User tony@domain.com Gets the list of users who have delegate access to Tony's inbox. #> [cmdletbinding()] Param ( [parameter(Mandatory = $false,Position = 0)] [Alias("From","Delegator")] [ValidateNotNullOrEmpty()] [String[]] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false,Position = 1)] [Alias("To")] [ValidateNotNullOrEmpty()] [String] $Delegate, [parameter(Mandatory = $false)] [switch] $NoGroupCheck ) Process { foreach ($U in $User) { if ($U -ceq 'me') { $U = $Script:PSGSuite.AdminEmail } elseif ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/gmail.settings.basic' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $U } $service = New-GoogleService @serviceParams if ($PSBoundParameters.Keys -contains 'Delegate') { try { Write-Verbose "Getting Gmail Delegate '$Delegate' for user '$U'" $request = $service.Users.Settings.Delegates.Get($U,$Delegate) $request.Execute() } catch { $origError = $_ if (!$NoGroupCheck -and ($group = Get-GSGroup -Group $U -Verbose:$false -ErrorAction SilentlyContinue)) { Write-Warning "$U is a group, not a user. You can only manage delegates for a user." } else { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($origError) } else { Write-Error $origError } } } } else { try { Write-Verbose "Getting Gmail Delegate list for user '$U'" $request = $service.Users.Settings.Delegates.List($U) $res = $request.Execute() if ($res.Delegates) { $res.Delegates | Add-Member -MemberType NoteProperty -Name Delegator -Value $U -Force -PassThru } else { Write-Warning "No delegates found for user '$U'" } } catch { $origError = $_ if (!$NoGroupCheck -and ($group = Get-GSGroup -Group $U -Verbose:$false -ErrorAction SilentlyContinue)) { Write-Warning "$U is a group, not a user. You can only manage delegates for a user." } else { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($origError) } else { Write-Error $origError } } } } } } } Export-ModuleMember -Function 'Get-GSGmailDelegate' function Remove-GSGmailDelegate { <# .SYNOPSIS Removes the specified delegate (which can be of any verification status), and revokes any verification that may have been required for using it. .DESCRIPTION Removes the specified delegate (which can be of any verification status), and revokes any verification that may have been required for using it. Note that a delegate user must be referred to by their primary email address, and not an email alias. .PARAMETER User User's email address to remove delegate access to .PARAMETER Delegate Delegate's email address to remove .EXAMPLE Remove-GSGmailDelegate -User tony@domain.com -Delegate peter@domain.com Removes Peter's access to Tony's inbox. #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $true,Position = 0)] [Alias("From","Delegator")] [ValidateNotNullOrEmpty()] [String] $User, [parameter(Mandatory = $true,Position = 1)] [Alias("To")] [ValidateNotNullOrEmpty()] [String] $Delegate ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } if ($Delegate -notlike "*@*.*") { $Delegate = "$($Delegate)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/gmail.settings.sharing' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $User } $service = New-GoogleService @serviceParams } Process { if ($PSCmdlet.ShouldProcess("Removing delegate access for '$Delegate' from user '$User's inbox")) { try { Write-Verbose "Removing delegate access for '$Delegate' from user '$User's inbox" $request = $service.Users.Settings.Delegates.Delete($User,$Delegate) $request.Execute() Write-Verbose "Successfully removed delegate access for user '$User's inbox for delegate '$Delegate'" } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($origError) } else { Write-Error $origError } } } } } Export-ModuleMember -Function 'Remove-GSGmailDelegate' function Add-GSDocContent { <# .SYNOPSIS Adds content to a Google Doc via appending new text. This does not overwrite existing content .DESCRIPTION Adds content to a Google Doc via appending new text. This does not overwrite existing content .PARAMETER FileID The unique Id of the file to add content to .PARAMETER Value The content to add .PARAMETER User The email or unique Id of the owner of the Drive file Defaults to the AdminEmail user .EXAMPLE $newLogStrings | Add-GSDocContent -FileId '1rhsAYTOB_vrpvfwImPmWy0TcVa2sgmQa_9u976' Appends the strings in the $newLogStrings variable to the existing the content of the specified Google Doc. #> [CmdLetBinding()] Param ( [parameter(Mandatory = $true,Position = 0)] [String] $FileID, [parameter(Mandatory = $true,ValueFromPipeline = $true)] [String[]] $Value, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias('Owner','PrimaryEmail','UserKey','Mail')] [string] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/drive' ServiceType = 'Google.Apis.Drive.v3.DriveService' User = $User } $service = New-GoogleService @serviceParams $stream = New-Object 'System.IO.MemoryStream' $writer = New-Object 'System.IO.StreamWriter' $stream $currentContent = Get-GSDocContent -FileID $FileID -User $User -Verbose:$false $concatStrings = @($currentContent) } Process { foreach ($string in $Value) { $concatStrings += $string } } End { try { $concatStrings = $concatStrings -join "`n" $writer.Write($concatStrings) $writer.Flush() $contentType = 'text/plain' $body = New-Object 'Google.Apis.Drive.v3.Data.File' $request = $service.Files.Update($body,$FileId,$stream,$contentType) $request.QuotaUser = $User $request.ChunkSize = 512KB $request.SupportsTeamDrives = $true Write-Verbose "Adding content to File '$FileID'" $request.Upload() | Out-Null $stream.Close() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Add-GSDocContent' function Add-GSDrivePermission { <# .SYNOPSIS Adds a new permission to a Drive file .DESCRIPTION Adds a new permission to a Drive file .PARAMETER User The owner of the Drive file Defaults to the AdminEmail user .PARAMETER FileId The unique Id of the Drive file you would like to add the permission to .PARAMETER Role The role/permission set you would like to give the email $EmailAddress Available values are: * "Owner" * "Writer" * "Commenter" * "Reader" * "Organizer" .PARAMETER Type The type of the grantee Available values are: * "User": a user email * "Group": a group email * "Domain": the entire domain * "Anyone": public access .PARAMETER EmailAddress The email address of the user or group to which this permission refers .PARAMETER Domain The domain to which this permission refers .PARAMETER ExpirationTime The time at which this permission will expire. Expiration times have the following restrictions: * They can only be set on user and group permissions * The time must be in the future * The time cannot be more than a year in the future .PARAMETER EmailMessage A plain text custom message to include in the notification email .PARAMETER SendNotificationEmail Whether to send a notification email when sharing to users or groups. This defaults to **FALSE** for users and groups in PSGSuite, and is not allowed for other requests. **It must not be disabled for ownership transfers** .PARAMETER AllowFileDiscovery Whether the permission allows the file to be discovered through search. This is only applicable for permissions of type domain or anyone .PARAMETER TransferOwnership Confirms transfer of ownership if the Role is set to 'Owner'. You can also force the same behavior by passing -Confirm:$false instead .PARAMETER UseDomainAdminAccess Whether the request should be treated as if it was issued by a domain administrator; if set to true, then the requester will be granted access if they are an administrator of the domain to which the item belongs .EXAMPLE Add-GSDrivePermission -FileId "1rhsAYTOB_vrpvfwImPmWy0TcVa2sgmQa_9u976" -Role Owner -Type User -EmailAddress joe -SendNotificationEmail -Confirm:$false Adds user joe@domain.com as the new owner of the file Id and sets the AdminEmail user as a Writer on the file #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High",DefaultParameterSetName = "Email")] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias('Owner','PrimaryEmail','UserKey','Mail')] [string] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $true)] [String] $FileId, [parameter(Mandatory = $true)] [ValidateSet("Owner","Writer","Commenter","Reader","Organizer")] [String] $Role, [parameter(Mandatory = $true)] [ValidateSet("User","Group","Domain","Anyone")] [String] $Type, [parameter(Mandatory = $false,ParameterSetName = "Email")] [String] $EmailAddress, [parameter(Mandatory = $false,ParameterSetName = "Domain")] [String] $Domain, [parameter(Mandatory = $false)] [DateTime] $ExpirationTime, [parameter(Mandatory = $false)] [string] $EmailMessage, [parameter(Mandatory = $false)] [Switch] $SendNotificationEmail, [parameter(Mandatory = $false)] [Switch] $AllowFileDiscovery, [parameter(Mandatory = $false)] [Alias('ConfirmTransferOfOwnership','TransferOfOwnership')] [switch] $TransferOwnership, [parameter(Mandatory = $false)] [switch] $UseDomainAdminAccess ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/drive' ServiceType = 'Google.Apis.Drive.v3.DriveService' User = $User } $service = New-GoogleService @serviceParams } Process { try { if ($Role -eq "Owner" -and !$TransferOwnership) { if ($PSCmdlet.ShouldProcess("Confirm transfer of ownership of FileId '$FileID' from user '$User' to user '$EmailAddress'")) { $PSBoundParameters['TransferOwnership'] = $true $TransferOwnership = $true } else { throw "The TransferOwnership parameter is required when setting the 'Owner' role." } } if (($Type -eq "User" -or $Type -eq "Group") -and !$EmailAddress) { throw "The EmailAddress parameter is required for types 'User' or 'Group'." } if (($Type -eq "User" -or $Type -eq "Group") -and ($PSBoundParameters.Keys -contains 'AllowFileDiscovery')) { Write-Warning "The AllowFileDiscovery parameter is only applicable for types 'Domain' or 'Anyone' This parameter will be excluded from this request." $PSBoundParameters.Remove('AllowFileDiscovery') | Out-Null } if ($TransferOwnership -and !$SendNotificationEmail) { $PSBoundParameters['SendNotificationEmail'] = $true Write-Warning "Setting SendNotificationEmail to 'True' to prevent errors (required for Ownership transfers)" } $body = New-Object 'Google.Apis.Drive.v3.Data.Permission' foreach ($key in $PSBoundParameters.Keys) { switch ($key) { EmailAddress { if ($EmailAddress -ceq 'me') { $EmailAddress = $Script:PSGSuite.AdminEmail } elseif ($EmailAddress -notlike "*@*.*") { $EmailAddress = "$($EmailAddress)@$($Script:PSGSuite.Domain)" } $body.EmailAddress = $EmailAddress } Role { $body.$key = ($PSBoundParameters[$key]).ToLower() } Type { $body.$key = ($PSBoundParameters[$key]).ToLower() } Default { if ($body.PSObject.Properties.Name -contains $key) { $body.$key = $PSBoundParameters[$key] } } } } $request = $service.Permissions.Create($body,$FileId) $request.SupportsTeamDrives = $true foreach ($key in $PSBoundParameters.Keys) { if ($request.PSObject.Properties.Name -contains $key -and $key -ne 'FileId') { $request.$key = $PSBoundParameters[$key] } } if ($PSBoundParameters.Keys -notcontains 'SendNotificationEmail') { $request.SendNotificationEmail = $false } Write-Verbose "Adding Drive Permission of '$Role' for user '$EmailAddress' on Id '$FileID'" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Add-GSDrivePermission' function Copy-GSDriveFile { <# .SYNOPSIS Make a copy of a file in Drive .DESCRIPTION Make a copy of a file in Drive .PARAMETER FileID The unique Id of the file to copy .PARAMETER User The email or unique Id of the owner of the Drive file Defaults to the AdminEmail user .PARAMETER Name The name of the new Drive file copy .PARAMETER Description The description of the new Drive file copy .PARAMETER Parents The parent Ids of the new Drive file copy .PARAMETER Projection The defined subset of fields to be returned Available values are: * "Minimal" * "Standard" * "Full" * "Access" .PARAMETER Fields The specific fields to returned .EXAMPLE Copy-GSDriveFile -FileId '1rhsAYTOB_vrpvfwImPmWy0TcVa2sgmQa_9u976' -Name "New Daily Checklist" Copies the Drive file Id to a new Drive file named 'New Daily Checklist' #> [cmdletbinding(DefaultParameterSetName = "Depth")] Param ( [parameter(Mandatory = $true,Position = 0)] [String] $FileID, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias('Owner','PrimaryEmail','UserKey','Mail')] [string] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false)] [String] $Name, [parameter(Mandatory = $false)] [String] $Description, [parameter(Mandatory = $false)] [String[]] $Parents, [parameter(Mandatory = $false,ParameterSetName = "Depth")] [Alias('Depth')] [ValidateSet("Minimal","Standard","Full","Access")] [String] $Projection = "Full", [parameter(Mandatory = $false,ParameterSetName = "Fields")] [ValidateSet("appProperties","capabilities","contentHints","createdTime","description","explicitlyTrashed","fileExtension","folderColorRgb","fullFileExtension","hasThumbnail","headRevisionId","iconLink","id","imageMediaMetadata","isAppAuthorized","kind","lastModifyingUser","md5Checksum","mimeType","modifiedByMe","modifiedByMeTime","modifiedTime","name","originalFilename","ownedByMe","owners","parents","permissions","properties","quotaBytesUsed","shared","sharedWithMeTime","sharingUser","size","spaces","starred","thumbnailLink","thumbnailVersion","trashed","version","videoMediaMetadata","viewedByMe","viewedByMeTime","viewersCanCopyContent","webContentLink","webViewLink","writersCanShare")] [String[]] $Fields ) Begin { if ($Projection) { $fs = switch ($Projection) { Standard { @("createdTime","description","fileExtension","id","lastModifyingUser","modifiedTime","name","owners","parents","properties","version","webContentLink","webViewLink") } Access { @("createdTime","description","fileExtension","id","lastModifyingUser","modifiedTime","name","ownedByMe","owners","parents","permissionIds","permissions","shared","sharedWithMeTime","sharingUser","viewedByMe","viewedByMeTime","viewersCanCopyContent","writersCanShare") } Full { @("appProperties","capabilities","contentHints","createdTime","description","explicitlyTrashed","fileExtension","folderColorRgb","fullFileExtension","hasAugmentedPermissions","hasThumbnail","headRevisionId","iconLink","id","imageMediaMetadata","isAppAuthorized","kind","lastModifyingUser","md5Checksum","mimeType","modifiedByMe","modifiedByMeTime","modifiedTime","name","originalFilename","ownedByMe","owners","parents","permissionIds","permissions","properties","quotaBytesUsed","shared","sharedWithMeTime","sharingUser","size","spaces","starred","teamDriveId","thumbnailLink","thumbnailVersion","trashed","trashedTime","trashingUser","version","videoMediaMetadata","viewedByMe","viewedByMeTime","viewersCanCopyContent","webContentLink","webViewLink","writersCanShare") } } } elseif ($Fields) { $fs = $Fields } if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/drive' ServiceType = 'Google.Apis.Drive.v3.DriveService' User = $User } $service = New-GoogleService @serviceParams } Process { try { $body = New-Object 'Google.Apis.Drive.v3.Data.File' if ($Name) { $body.Name = $Name } if ($Description) { $body.Description = $Description } if ($Parents) { $body.Parents = [String[]]$Parents } $request = $service.Files.Copy($body,$FileID) $request.SupportsTeamDrives = $true if ($fs) { $request.Fields = "$($fs -join ",")" } Write-Verbose "Copying drive file id '$FileID'" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Copy-GSDriveFile' function Export-GSDriveFile { <# .SYNOPSIS Exports a Drive file as if you chose "Export" from the File menu when viewing the file .DESCRIPTION Exports a Drive file as if you chose "Export" from the File menu when viewing the file .PARAMETER FileID The unique Id of the file to export .PARAMETER User The email or unique Id of the owner of the Drive file Defaults to the AdminEmail user .PARAMETER Type The type of local file you would like to export the Drive file as Available values are: * "CSV" * "HTML" * "JPEG" * "JSON" * "MSExcel" * "MSPowerPoint" * "MSWordDoc" * "OpenOfficeDoc" * "OpenOfficeSheet" * "PDF" * "PlainText" * "PNG" * "RichText" * "SVG" .PARAMETER OutFilePath The directory path that you would like to export the Drive file to Defaults to the current working directory .PARAMETER Projection The defined subset of fields to be returned Available values are: * "Minimal" * "Standard" * "Full" * "Access" .PARAMETER Fields The specific fields to returned .EXAMPLE Export-GSDriveFile -FileId '1rhsAYTOB_vrpvfwImPmWy0TcVa2sgmQa_9u976' -Type CSV -OutFilePath .\SheetExport.csv Exports the Drive file as a CSV to the current working directory #> [CmdLetBinding(DefaultParameterSetName = "Depth")] Param ( [parameter(Mandatory = $true,Position = 0)] [String] $FileID, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias('Owner','PrimaryEmail','UserKey','Mail')] [string] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $true)] [ValidateSet("CSV","EPUB","HTML","HTMLZipped","JPEG","JSON","MSExcel","MSPowerPoint","MSWordDoc","OpenOfficeDoc","OpenOfficePresentation","OpenOfficeSheet","PDF","PlainText","PNG","RichText","SVG","TSV")] [String] $Type, [parameter(Mandatory = $false)] [String] $OutFilePath, [parameter(Mandatory = $false,ParameterSetName = "Depth")] [Alias('Depth')] [ValidateSet("Minimal","Standard","Full","Access")] [String] $Projection = "Full", [parameter(Mandatory = $false,ParameterSetName = "Fields")] [ValidateSet("appProperties","capabilities","contentHints","createdTime","description","explicitlyTrashed","fileExtension","folderColorRgb","fullFileExtension","hasThumbnail","headRevisionId","iconLink","id","imageMediaMetadata","isAppAuthorized","kind","lastModifyingUser","md5Checksum","mimeType","modifiedByMe","modifiedByMeTime","modifiedTime","name","originalFilename","ownedByMe","owners","parents","permissions","properties","quotaBytesUsed","shared","sharedWithMeTime","sharingUser","size","spaces","starred","thumbnailLink","thumbnailVersion","trashed","version","videoMediaMetadata","viewedByMe","viewedByMeTime","viewersCanCopyContent","webContentLink","webViewLink","writersCanShare")] [String[]] $Fields, [parameter(Mandatory = $false)] [Switch] $Force ) Begin { if ($Projection) { $fs = switch ($Projection) { Standard { @("createdTime","description","fileExtension","id","lastModifyingUser","modifiedTime","name","owners","parents","properties","version","webContentLink","webViewLink") } Access { @("createdTime","description","fileExtension","id","lastModifyingUser","modifiedTime","name","ownedByMe","owners","parents","permissionIds","permissions","shared","sharedWithMeTime","sharingUser","viewedByMe","viewedByMeTime","viewersCanCopyContent","writersCanShare") } Full { @("appProperties","capabilities","contentHints","createdTime","description","explicitlyTrashed","fileExtension","folderColorRgb","fullFileExtension","hasAugmentedPermissions","hasThumbnail","headRevisionId","iconLink","id","imageMediaMetadata","isAppAuthorized","kind","lastModifyingUser","md5Checksum","mimeType","modifiedByMe","modifiedByMeTime","modifiedTime","name","originalFilename","ownedByMe","owners","parents","permissionIds","permissions","properties","quotaBytesUsed","shared","sharedWithMeTime","sharingUser","size","spaces","starred","teamDriveId","thumbnailLink","thumbnailVersion","trashed","trashedTime","trashingUser","version","videoMediaMetadata","viewedByMe","viewedByMeTime","viewersCanCopyContent","webContentLink","webViewLink","writersCanShare") } } } elseif ($Fields) { $fs = $Fields } $mimeHash = @{ CSV = "text/csv" EPUB = "application/epub+zip" HTML = "text/html" HTMLZipped = "application/zip" JPEG = "image/jpeg" JSON = "application/vnd.google-apps.script+json" MSExcel = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" MSPowerPoint = "application/vnd.openxmlformats-officedocument.presentationml.presentation" MSWordDoc = "application/vnd.openxmlformats-officedocument.wordprocessingml.document" OpenOfficeDoc = "application/vnd.oasis.opendocument.text" OpenOfficePresentation = "application/vnd.oasis.opendocument.presentation" OpenOfficeSheet = "application/x-vnd.oasis.opendocument.spreadsheet" PDF = "application/pdf" PlainText = "text/plain" PNG = "image/png" RichText = "application/rtf" SVG = "image/svg+xml" TSV = "text/tab-separated-values" } if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/drive' ServiceType = 'Google.Apis.Drive.v3.DriveService' User = $User } $service = New-GoogleService @serviceParams } Process { try { $request = $service.Files.Export($FileID,($mimeHash[$Type])) if ($fs) { $request.Fields = $($fs -join ",") } if ($OutFilePath) { if ((Test-Path $OutFilePath) -and !$Force) { throw "File '$OutFilePath' already exists. If you would like to overwrite it, use the -Force parameter." } else { Write-Verbose "Saving file to path '$OutFilePath'" $stream = [System.IO.File]::Create($OutFilePath) $request.Download($stream) $stream.Close() } } else { Write-Verbose "Getting content of File '$FileID' as Type '$Type'" $request.Execute() } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Export-GSDriveFile' function Get-GSDocContent { <# .SYNOPSIS Gets the content of a Google Doc and returns it as an array of strings. Supports HTML or PlainText .DESCRIPTION Gets the content of a Google Doc and returns it as an array of strings. Supports HTML or PlainText .PARAMETER FileID The unique Id of the file to get content of .PARAMETER User The email or unique Id of the owner of the Drive file Defaults to the AdminEmail user .EXAMPLE Get-GSDocContent -FileId '1rhsAYTOB_vrpvfwImPmWy0TcVa2sgmQa_9u976' Exports the Drive file as a CSV to the current working directory #> [CmdLetBinding()] Param ( [parameter(Mandatory = $true,Position = 0)] [String] $FileID, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias('Owner','PrimaryEmail','UserKey','Mail')] [string] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false)] [ValidateSet("HTML","PlainText")] [String] $Type ) Begin { $typeParam = @{} if ($PSBoundParameters.Keys -notcontains 'Type') { $typeParam['Type'] = "PlainText" } } Process { try { (Export-GSDriveFile @PSBoundParameters -Projection Minimal @typeParam) -split "`n" Write-Verbose "Content retrieved for File '$FileID'" } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSDocContent' function Get-GSDriveFile { <# .SYNOPSIS Gets information about or downloads a Drive file .DESCRIPTION Gets information about or downloads a Drive file .PARAMETER FileId The unique Id of the file to get .PARAMETER User The email or unique Id of the owner of the Drive file Defaults to the AdminEmail user .PARAMETER OutFilePath The directory path that you would like to download the Drive file to. If excluded, only the Drive file information will be returned .PARAMETER Projection The defined subset of fields to be returned Available values are: * "Minimal" * "Standard" * "Full" * "Access" .PARAMETER Fields The specific fields to returned .EXAMPLE Get-GSDriveFile -FileId '1rhsAYTOB_vrpvfwImPmWy0TcVa2sgmQa_9u976' Gets the information for the file .EXAMPLE Get-GSDriveFile -FileId '1rhsAYTOB_vrpvfwImPmWy0TcVa2sgmQa_9u976' -OutFilePath (Get-Location).Path Gets the information for the file and saves the file in the current working directory #> [cmdletbinding(DefaultParameterSetName = "Depth")] Param ( [parameter(Mandatory = $true,Position = 0)] [String[]] $FileId, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias('Owner','PrimaryEmail','UserKey','Mail')] [string] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false)] [Alias('SaveFileTo')] [ValidateScript({(Get-Item $_).PSIsContainer})] [String] $OutFilePath, [parameter(Mandatory = $false,ParameterSetName = "Depth")] [Alias('Depth')] [ValidateSet("Minimal","Standard","Full","Access")] [String] $Projection = "Full", [parameter(Mandatory = $false,ParameterSetName = "Fields")] [ValidateSet("appProperties","capabilities","contentHints","createdTime","description","explicitlyTrashed","fileExtension","folderColorRgb","fullFileExtension","hasThumbnail","headRevisionId","iconLink","id","imageMediaMetadata","isAppAuthorized","kind","lastModifyingUser","md5Checksum","mimeType","modifiedByMe","modifiedByMeTime","modifiedTime","name","originalFilename","ownedByMe","owners","parents","permissions","properties","quotaBytesUsed","shared","sharedWithMeTime","sharingUser","size","spaces","starred","thumbnailLink","thumbnailVersion","trashed","version","videoMediaMetadata","viewedByMe","viewedByMeTime","viewersCanCopyContent","webContentLink","webViewLink","writersCanShare")] [String[]] $Fields ) Begin { if ($Projection) { $fs = switch ($Projection) { Standard { @("createdTime","description","fileExtension","id","lastModifyingUser","modifiedTime","name","owners","parents","properties","version","webContentLink","webViewLink") } Access { @("createdTime","description","fileExtension","id","lastModifyingUser","modifiedTime","name","ownedByMe","owners","parents","permissionIds","permissions","shared","sharedWithMeTime","sharingUser","viewedByMe","viewedByMeTime","viewersCanCopyContent","writersCanShare") } Full { @("appProperties","capabilities","contentHints","createdTime","description","explicitlyTrashed","fileExtension","folderColorRgb","fullFileExtension","hasAugmentedPermissions","hasThumbnail","headRevisionId","iconLink","id","imageMediaMetadata","isAppAuthorized","kind","lastModifyingUser","md5Checksum","mimeType","modifiedByMe","modifiedByMeTime","modifiedTime","name","originalFilename","ownedByMe","owners","parents","permissionIds","permissions","properties","quotaBytesUsed","shared","sharedWithMeTime","sharingUser","size","spaces","starred","teamDriveId","thumbnailLink","thumbnailVersion","trashed","trashedTime","trashingUser","version","videoMediaMetadata","viewedByMe","viewedByMeTime","viewersCanCopyContent","webContentLink","webViewLink","writersCanShare") } } } elseif ($Fields) { $fs = $Fields } if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/drive' ServiceType = 'Google.Apis.Drive.v3.DriveService' User = $User } $service = New-GoogleService @serviceParams } Process { try { foreach ($file in $FileId) { $request = $service.Files.Get($file) $request.SupportsTeamDrives = $true if ($fs) { $request.Fields = $($fs -join ",") } $res = $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru if ($OutFilePath -and $res.FileExtension) { $resPath = Resolve-Path $OutFilePath $filePath = Join-Path $resPath "$($res.Name).$($res.FileExtension)" Write-Verbose "Saving file to path '$filePath'" $stream = [System.IO.File]::Create($filePath) $request.Download($stream) $stream.Close() } $res } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSDriveFile' function Get-GSDriveFileList { <# .SYNOPSIS Gets the list of Drive files owned by the user .DESCRIPTION Gets the list of Drive files owned by the user .PARAMETER User The email or unique Id of the user whose Drive files you are trying to list Defaults to the AdminEmail user .PARAMETER Filter A query for filtering the file results. See the "Search for Files and Team Drives" guide for the supported syntax: https://developers.google.com/drive/v3/web/search-parameters PowerShell filter syntax here is supported as "best effort". Please use Google's filter operators and syntax to ensure best results .PARAMETER TeamDriveId ID of Team Drive to search .PARAMETER ParentFolderId ID of parent folder to search to add to the filter .PARAMETER IncludeTeamDriveItems Whether Team Drive items should be included in results. (Default: false) .PARAMETER Corpora Comma-separated list of bodies of items (files/documents) to which the query applies. Supported bodies are 'User', 'Domain', 'TeamDrive' and 'AllTeamDrives'. 'AllTeamDrives' must be combined with 'User'; all other values must be used in isolation. Prefer 'User' or 'TeamDrive' to 'AllTeamDrives' for efficiency. .PARAMETER Spaces A comma-separated list of spaces to query within the corpus. Supported values are 'Drive', 'AppDataFolder' and 'Photos'. .PARAMETER OrderBy A comma-separated list of sort keys. Valid keys are 'createdTime', 'folder', 'modifiedByMeTime', 'modifiedTime', 'name', 'name_natural', 'quotaBytesUsed', 'recency', 'sharedWithMeTime', 'starred', and 'viewedByMeTime'. .PARAMETER PageSize The page size of the result set .EXAMPLE Get-GSDriveFileList joe Gets Joe's Drive file list #> [cmdletbinding()] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias('Owner','PrimaryEmail','UserKey','Mail')] [string] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false)] [Alias('Q','Query')] [String[]] $Filter, [parameter(Mandatory = $false)] [String] $TeamDriveId, [parameter(Mandatory = $false)] [String] $ParentFolderId, [parameter(Mandatory = $false)] [Switch] $IncludeTeamDriveItems, [parameter(Mandatory = $false)] [ValidateSet('user','domain','teamDrive')] [String] $Corpora, [parameter(Mandatory = $false)] [ValidateSet('drive','appDataFolder','photos')] [String[]] $Spaces, [parameter(Mandatory = $false)] [ValidateSet('createdTime','folder','modifiedByMeTime','modifiedTime','name','quotaBytesUsed','recency','sharedWithMeTime','starred','viewedByMeTime')] [String[]] $OrderBy, [parameter(Mandatory = $false)] [Alias('MaxResults')] [ValidateRange(1,1000)] [Int] $PageSize = "1000" ) Begin { if ($TeamDriveId) { $PSBoundParameters['Corpora'] = 'teamDrive' $PSBoundParameters['IncludeTeamDriveItems'] = $true } if ($ParentFolderId) { if ($Filter) { $Filter += "'$ParentFolderId' in parents" $PSBoundParameters['Filter'] += "'$ParentFolderId' in parents" } else { $Filter = "'$ParentFolderId' in parents" $PSBoundParameters['Filter'] = "'$ParentFolderId' in parents" } } if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/drive' ServiceType = 'Google.Apis.Drive.v3.DriveService' User = $User } $service = New-GoogleService @serviceParams } Process { try { $request = $service.Files.List() $request.SupportsTeamDrives = $true $request.Fields = 'files,kind,nextPageToken' if ($PageSize) { $request.PageSize = $PageSize } foreach ($key in $PSBoundParameters.Keys) { switch ($key) { Filter { $FilterFmt = $PSBoundParameters[$key] -replace " -eq ","=" -replace " -like ",":" -replace " -match ",":" -replace " -contains ",":" -creplace "'True'","True" -creplace "'False'","False" -replace " -in "," in " -replace " -le ",'<=' -replace " -ge ",">=" -replace " -gt ",'>' -replace " -lt ",'<' -replace " -ne ","!=" -replace " -and "," and " -replace " -or "," or " -replace " -not "," not " $request.Q = $($FilterFmt -join " ") } Spaces { $request.$key = $($PSBoundParameters[$key] -join ",") } Default { if ($request.PSObject.Properties.Name -contains $key) { $request.$key = $PSBoundParameters[$key] } } } } if ($Filter) { Write-Verbose "Getting all Drive Files matching filter '$Filter' for user '$User'" } else { Write-Verbose "Getting all Drive Files for user '$User'" } [int]$i = 1 do { $result = $request.Execute() $result.Files | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru if ($result.NextPageToken) { $request.PageToken = $result.NextPageToken } [int]$retrieved = ($i + $result.Files.Count) - 1 Write-Verbose "Retrieved $retrieved Files..." [int]$i = $i + $result.Files.Count } until (!$result.NextPageToken) } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSDriveFileList' function Get-GSDriveFileUploadStatus { <# .SYNOPSIS Gets the current Drive file upload status .DESCRIPTION Gets the current Drive file upload status .PARAMETER Id The upload Id for the task you'd like to retrieve the status of .PARAMETER InProgress If passed, only returns upload statuses that are not 'Failed' or 'Completed'. If nothing is returned when passing this parameter, all tracked uploads have stopped .EXAMPLE Get-GSDriveFileUploadStatus -InProgress Gets the upload status for all tasks currently in progress #> [CmdletBinding()] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Int[]] $Id, [parameter(Mandatory = $false)] [Switch] $InProgress ) Begin { Write-Verbose "Getting Drive File Upload status" } Process { if ($script:DriveUploadTasks) { foreach ($task in $script:DriveUploadTasks) { $elapsed = ((Get-Date) - $task.StartTime) $progress = {$task.Request.GetProgress()}.InvokeReturnAsIs() $bytesSent = $progress.BytesSent $remaining = try { New-TimeSpan -Seconds $(($elapsed.TotalSeconds / ($bytesSent / ($task.Length))) - $elapsed.TotalSeconds) -ErrorAction Stop } catch { New-TimeSpan } $percentComplete = if ($bytesSent) { [Math]::Round((($bytesSent / $task.Length) * 100),4) } else { 0 } if ($Id) { if ($Id -contains $task.Id) { $obj = [PSCustomObject]@{ Id = $task.Id Status = $progress.Status PercentComplete = $percentComplete Remaining = $remaining StartTime = $task.StartTime Elapsed = $elapsed File = $task.File.FullName Length = $task.Length Parents = $task.Parents BytesSent = $bytesSent FileLocked = $(Test-FileLock -Path $task.File) User = $task.User Exception = $progress.Exception } if (!$InProgress -or $obj.Status -notin @('Failed','Completed')) { $obj } } } else { $obj = [PSCustomObject]@{ Id = $task.Id Status = $progress.Status PercentComplete = $percentComplete Remaining = $remaining StartTime = $task.StartTime Elapsed = $elapsed File = $task.File.FullName Length = $task.Length Parents = $task.Parents BytesSent = $bytesSent FileLocked = $(Test-FileLock -Path $task.File) User = $task.User Exception = $progress.Exception } if (!$InProgress -or $obj.Status -notin @('Failed','Completed')) { $obj } } } } } } Export-ModuleMember -Function 'Get-GSDriveFileUploadStatus' function Get-GSDrivePermission { <# .SYNOPSIS Gets permission information for a Drive file .DESCRIPTION Gets permission information for a Drive file .PARAMETER User The email or unique Id of the user whose Drive file permission you are trying to get Defaults to the AdminEmail user .PARAMETER FileId The unique Id of the Drive file .PARAMETER PermissionId The unique Id of the permission you are trying to get. If excluded, the list of permissions for the Drive file will be returned instead .PARAMETER PageSize The page size of the result set .EXAMPLE Get-GSDrivePermission -FileId '1rhsAYTOB_vrpvfwImPmWy0TcVa2sgmQa_9u976' Gets the list of permissions for the file Id #> [cmdletbinding(DefaultParameterSetName = "List")] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias('Owner','PrimaryEmail','UserKey','Mail')] [string] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $true)] [String] $FileId, [parameter(Mandatory = $false,ParameterSetName = "Get")] [String[]] $PermissionId, [parameter(Mandatory = $false,ParameterSetName = "List")] [Alias('MaxResults')] [ValidateRange(1,100)] [Int] $PageSize = "100" ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/drive' ServiceType = 'Google.Apis.Drive.v3.DriveService' User = $User } $service = New-GoogleService @serviceParams } Process { try { if ($PermissionId) { foreach ($per in $PermissionId) { $request = $service.Permissions.Get($FileId,$per) $request.SupportsTeamDrives = $true $request.Fields = "*" Write-Verbose "Getting Permission Id '$per' on File '$FileId' for user '$User'" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru | Add-Member -MemberType NoteProperty -Name 'FileId' -Value $FileId -PassThru } } else { $request = $service.Permissions.List($FileId) $request.SupportsTeamDrives = $true $request.PageSize = $PageSize $request.Fields = "*" Write-Verbose "Getting Permission list on File '$FileId' for user '$User'" [int]$i = 1 do { $result = $request.Execute() $result.Permissions | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru | Add-Member -MemberType NoteProperty -Name 'FileId' -Value $FileId -PassThru if ($result.NextPageToken) { $request.PageToken = $result.NextPageToken } [int]$retrieved = ($i + $result.Permissions.Count) - 1 Write-Verbose "Retrieved $retrieved Permissions..." [int]$i = $i + $result.Permissions.Count } until (!$result.NextPageToken) } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSDrivePermission' function Get-GSDriveProfile { <# .SYNOPSIS Gets Drive profile for the user .DESCRIPTION Gets Drive profile for the user .PARAMETER User The user to get profile of Defaults to the AdminEmail user .EXAMPLE Get-GSDriveProfile Gets the Drive profile of the AdminEmail user #> [cmdletbinding()] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias('Owner','PrimaryEmail','UserKey','Mail')] [string] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false,Position = 1)] [ValidateSet('AppInstalled','ExportFormats','FolderColorPalette','ImportFormats','Kind','MaxImportSizes','MaxUploadSize','StorageQuota','TeamDriveThemes','User')] [string[]] $Fields = @('AppInstalled','ExportFormats','FolderColorPalette','ImportFormats','Kind','MaxImportSizes','MaxUploadSize','StorageQuota','TeamDriveThemes','User') ) Begin { $fieldDict = @{ AppInstalled = 'appInstalled' ExportFormats = 'exportFormats' FolderColorPalette = 'folderColorPalette' ImportFormats = 'importFormats' Kind = 'kind' MaxImportSizes = 'maxImportSizes' MaxUploadSize = 'maxUploadSize' StorageQuota = 'storageQuota' TeamDriveThemes = 'teamDriveThemes' User = 'user' } } Process { foreach ($U in $User) { if ($U -ceq 'me') { $U = $Script:PSGSuite.AdminEmail } elseif ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/drive' ServiceType = 'Google.Apis.Drive.v3.DriveService' User = $U } $service = New-GoogleService @serviceParams try { $request = $service.About.Get() $request.Fields = "$(($Fields | ForEach-Object {$fieldDict[$_]}) -join ",")" Write-Verbose "Getting Drive profile for user '$U'" $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Get-GSDriveProfile' function Get-GSTeamDrive { <# .SYNOPSIS Gets information about a Team Drive .DESCRIPTION Gets information about a Team Drive .PARAMETER TeamDriveId The unique Id of the Team Drive. If excluded, the list of Team Drives will be returned .PARAMETER User The email or unique Id of the user with access to the Team Drive .PARAMETER Filter Query string for searching Team Drives. See the "Search for Files and Team Drives" guide for the supported syntax: https://developers.google.com/drive/v3/web/search-parameters PowerShell filter syntax here is supported as "best effort". Please use Google's filter operators and syntax to ensure best results .PARAMETER PageSize The page size of the result set .EXAMPLE #> [cmdletbinding(DefaultParameterSetName = "List")] Param ( [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true,ParameterSetName = "Get")] [Alias('Id')] [String[]] $TeamDriveId, [parameter(Mandatory = $false,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias('Owner','PrimaryEmail','UserKey','Mail')] [string] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false,ParameterSetName = "List")] [Alias('Q','Query')] [String] $Filter, [parameter(Mandatory = $false,ParameterSetName = "List")] [ValidateRange(1,100)] [Int] $PageSize = "100" ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/drive' ServiceType = 'Google.Apis.Drive.v3.DriveService' User = $User } $service = New-GoogleService @serviceParams } Process { try { switch ($PSCmdlet.ParameterSetName) { Get { foreach ($id in $TeamDriveId) { $request = $service.Teamdrives.Get($id) Write-Verbose "Getting Team Drive '$id' for user '$User'" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } } List { $request = $service.Teamdrives.List() $request.PageSize = $PageSize if ($Filter) { $FilterFmt = $Filter -replace " -eq ","=" -replace " -like "," contains " -replace " -match "," contains " -replace " -contains "," contains " -creplace "'True'","True" -creplace "'False'","False" -replace " -in "," in " -replace " -le ",'<=' -replace " -ge ",">=" -replace " -gt ",'>' -replace " -lt ",'<' -replace " -ne ","!=" -replace " -and "," and " -replace " -or "," or " -replace " -not "," not " $request.UseDomainAdminAccess = $true $request.Q = $($FilterFmt -join " ") } Write-Verbose "Getting Team Drives for user '$User'" $request.Execute() | Select-Object -ExpandProperty TeamDrives | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSTeamDrive' function New-GSDriveFile { <# .SYNOPSIS Creates a blank Drive file .DESCRIPTION Creates a blank Drive file .PARAMETER User The email or unique Id of the user who you are creating the Drive file for Defaults to the AdminEmail user .PARAMETER Name The name of the new Drive file .PARAMETER Parents The parent folder Id of the new Drive file .PARAMETER MimeType The Google Mime Type of the new Drive file Available values are: * "Audio" * "Docs" * "Drawing" * "DriveFile" * "DriveFolder" * "Form" * "FusionTables" * "Map" * "Photo" * "Slides" * "AppsScript" * "Sites" * "Sheets" * "Unknown" * "Video" .PARAMETER CustomMimeType The custom Mime Type of the new Drive file .PARAMETER Projection The defined subset of fields to be returned Available values are: * "Minimal" * "Standard" * "Full" * "Access" .PARAMETER Fields The specific fields to returned .EXAMPLE New-GSDriveFile -Name "Training Docs" -MimeType DriveFolder Creates a new folder in Drive named "Training Docs" in the root OrgUnit for the AdminEmail user #> [cmdletbinding(DefaultParameterSetName = "BuiltIn")] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias('Owner','PrimaryEmail','UserKey','Mail')] [string] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $true)] [String] $Name, [parameter(Mandatory = $false)] [Alias('ParentId')] [String[]] $Parents, [parameter(Mandatory = $true,ParameterSetName = "BuiltIn")] [Alias('Type')] [ValidateSet("Audio","Docs","Drawing","DriveFile","DriveFolder","Form","FusionTables","Map","Photo","Slides","AppsScript","Sites","Sheets","Unknown","Video")] [String] $MimeType, [parameter(Mandatory = $true,ParameterSetName = "Custom")] [String] $CustomMimeType, [parameter(Mandatory = $false)] [Alias('Depth')] [ValidateSet("Minimal","Standard","Full","Access")] [String] $Projection = "Full", [parameter(Mandatory = $false)] [ValidateSet("appProperties","capabilities","contentHints","createdTime","description","explicitlyTrashed","fileExtension","folderColorRgb","fullFileExtension","hasThumbnail","headRevisionId","iconLink","id","imageMediaMetadata","isAppAuthorized","kind","lastModifyingUser","md5Checksum","mimeType","modifiedByMe","modifiedByMeTime","modifiedTime","name","originalFilename","ownedByMe","owners","parents","permissions","properties","quotaBytesUsed","shared","sharedWithMeTime","sharingUser","size","spaces","starred","thumbnailLink","thumbnailVersion","trashed","version","videoMediaMetadata","viewedByMe","viewedByMeTime","viewersCanCopyContent","webContentLink","webViewLink","writersCanShare")] [String[]] $Fields ) Begin { $mimeHash = @{ Audio = "application/vnd.google-apps.audio" Docs = "application/vnd.google-apps.document" Drawing = "application/vnd.google-apps.drawing" DriveFile = "application/vnd.google-apps.file" DriveFolder = "application/vnd.google-apps.folder" Form = "application/vnd.google-apps.form" FusionTables = "application/vnd.google-apps.fusiontable" Map = "application/vnd.google-apps.map" Photo = "application/vnd.google-apps.photo" Slides = "application/vnd.google-apps.presentation" AppsScript = "application/vnd.google-apps.script" Sites = "application/vnd.google-apps.sites" Sheets = "application/vnd.google-apps.spreadsheet" Unknown = "application/vnd.google-apps.unknown" Video = "application/vnd.google-apps.video" } if ($Projection) { $fs = switch ($Projection) { Standard { @("createdTime","description","fileExtension","id","lastModifyingUser","modifiedTime","name","owners","parents","properties","version","webContentLink","webViewLink") } Access { @("createdTime","description","fileExtension","id","lastModifyingUser","modifiedTime","name","ownedByMe","owners","parents","permissionIds","permissions","shared","sharedWithMeTime","sharingUser","viewedByMe","viewedByMeTime","viewersCanCopyContent","writersCanShare") } Full { @("appProperties","capabilities","contentHints","createdTime","description","explicitlyTrashed","fileExtension","folderColorRgb","fullFileExtension","hasAugmentedPermissions","hasThumbnail","headRevisionId","iconLink","id","imageMediaMetadata","isAppAuthorized","kind","lastModifyingUser","md5Checksum","mimeType","modifiedByMe","modifiedByMeTime","modifiedTime","name","originalFilename","ownedByMe","owners","parents","permissionIds","permissions","properties","quotaBytesUsed","shared","sharedWithMeTime","sharingUser","size","spaces","starred","teamDriveId","thumbnailLink","thumbnailVersion","trashed","trashedTime","trashingUser","version","videoMediaMetadata","viewedByMe","viewedByMeTime","viewersCanCopyContent","webContentLink","webViewLink","writersCanShare") } } } elseif ($Fields) { $fs = $Fields } if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/drive' ServiceType = 'Google.Apis.Drive.v3.DriveService' User = $User } $service = New-GoogleService @serviceParams } Process { try { $body = New-Object 'Google.Apis.Drive.v3.Data.File' if ($MimeType) { $body.MimeType = $mimeHash[$MimeType] } elseif ($CustomMimeType) { $body.MimeType = $CustomMimeType } if ($Name) { $body.Name = [String]$Name } if ($Parents) { $body.Parents = [String[]]$Parents } $request = $service.Files.Create($body) $request.SupportsTeamDrives = $true if ($fs) { $request.Fields = $($fs -join ",") } Write-Verbose "Creating file '$Name' for user '$User'" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'New-GSDriveFile' function New-GSTeamDrive { <# .SYNOPSIS Creates a new Team Drive .DESCRIPTION Creates a new Team Drive .PARAMETER Name The name of the Team Drive .PARAMETER User The user to create the Team Drive for (must have permissions to create Team Drives) .PARAMETER RequestId An ID, such as a random UUID, which uniquely identifies this user's request for idempotent creation of a Team Drive. A repeated request by the same user and with the same request ID will avoid creating duplicates by attempting to create the same Team Drive. If the Team Drive already exists a 409 error will be returned. .PARAMETER CanAddChildren Whether the current user can add children to folders in this Team Drive .PARAMETER CanChangeTeamDriveBackground Whether the current user can change the background of this Team Drive .PARAMETER CanComment Whether the current user can comment on files in this Team Drive .PARAMETER CanCopy Whether the current user can copy files in this Team Drive .PARAMETER CanDeleteTeamDrive Whether the current user can delete this Team Drive. Attempting to delete the Team Drive may still fail if there are untrashed items inside the Team Drive .PARAMETER CanDownload Whether the current user can download files in this Team Drive .PARAMETER CanEdit Whether the current user can edit files in this Team Drive .PARAMETER CanListChildren Whether the current user can list the children of folders in this Team Drive .PARAMETER CanManageMembers Whether the current user can add members to this Team Drive or remove them or change their role .PARAMETER CanReadRevisions Whether the current user can read the revisions resource of files in this Team Drive .PARAMETER CanRemoveChildren Whether the current user can remove children from folders in this Team Drive .PARAMETER CanRename Whether the current user can rename files or folders in this Team Drive .PARAMETER CanRenameTeamDrive Whether the current user can rename this Team Drive .PARAMETER CanShare Whether the current user can share files or folders in this Team Drive .EXAMPLE New-GSTeamDrive -Name "Training Docs" Creates a new Team Drive named "Training Docs" #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0)] [String] $Name, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias('Owner','PrimaryEmail','UserKey','Mail')] [string] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [String] $RequestId = (New-Guid).ToString('N'), [parameter(Mandatory = $false)] [Switch] $CanAddChildren, [parameter(Mandatory = $false)] [Switch] $CanChangeTeamDriveBackground, [parameter(Mandatory = $false)] [Switch] $CanComment, [parameter(Mandatory = $false)] [Switch] $CanCopy, [parameter(Mandatory = $false)] [Switch] $CanDeleteTeamDrive, [parameter(Mandatory = $false)] [Switch] $CanDownload, [parameter(Mandatory = $false)] [Switch] $CanEdit, [parameter(Mandatory = $false)] [Switch] $CanListChildren, [parameter(Mandatory = $false)] [Switch] $CanManageMembers, [parameter(Mandatory = $false)] [Switch] $CanReadRevisions, [parameter(Mandatory = $false)] [Switch] $CanRemoveChildren, [parameter(Mandatory = $false)] [Switch] $CanRename, [parameter(Mandatory = $false)] [Switch] $CanRenameTeamDrive, [parameter(Mandatory = $false)] [Switch] $CanShare ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/drive' ServiceType = 'Google.Apis.Drive.v3.DriveService' User = $User } $service = New-GoogleService @serviceParams } Process { try { $body = New-Object 'Google.Apis.Drive.v3.Data.TeamDrive' $capabilities = New-Object 'Google.Apis.Drive.v3.Data.TeamDrive+CapabilitiesData' foreach ($key in $PSBoundParameters.Keys) { switch ($key) { Default { if ($capabilities.PSObject.Properties.Name -contains $key) { $capabilities.$key = $PSBoundParameters[$key] } elseif ($body.PSObject.Properties.Name -contains $key) { $body.$key = $PSBoundParameters[$key] } } } } $body.Capabilities = $capabilities $request = $service.Teamdrives.Create($body,$RequestId) Write-Verbose "Creating Team Drive '$Name' for user '$User'" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru | Add-Member -MemberType NoteProperty -Name 'RequestId' -Value $RequestId -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'New-GSTeamDrive' function Remove-GSDrivePermission { <# .SYNOPSIS Removes a permission from a Drive file .DESCRIPTION Removes a permission from a Drive file .PARAMETER User The email or unique Id of the user whose Drive file permission you are trying to get Defaults to the AdminEmail user .PARAMETER FileId The unique Id of the Drive file .PARAMETER PermissionId The unique Id of the permission you are trying to remove. .EXAMPLE Remove-GSDrivePermission -FileId '1rhsAYTOB_vrpvfwImPmWy0TcVa2sgmQa_9u976' -PermissionID 'sdfadsfsdafasd' Removes the permission from the drive. .EXAMPLE Get-GSDrivePermission -FileId '1rhsAYTOB_vrpvfwImPmWy0TcVa2sgmQa_9u976' | ? {$_.Type -eq 'group'} | Remove-GSDrivePermission Gets the permissions assigned to groups and removes them. #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias('Owner','PrimaryEmail','UserKey','Mail')] [string] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)] [String] $FileId, [parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)] [Alias('Id')] [String] $PermissionId ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/drive' ServiceType = 'Google.Apis.Drive.v3.DriveService' User = $User } $service = New-GoogleService @serviceParams } Process { try { if ($PSCmdlet.ShouldProcess("Removing Drive Permission Id '$PermissionId' from FileId '$FileID'")) { $request = $service.Permissions.Delete($FileId,$PermissionId) $request.SupportsTeamDrives = $true $request.Execute() Write-Verbose "Successfully removed Drive Permission Id '$PermissionId' from FileId '$FileID'" } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Remove-GSDrivePermission' function Remove-GSTeamDrive { <# .SYNOPSIS Removes a Team Drive .DESCRIPTION Removes a Team Drive .PARAMETER TeamDriveId The Id of the Team Drive to remove .PARAMETER User The email or unique Id of the user with permission to delete the Team Drive Defaults to the AdminEmail user .EXAMPLE Remove-TeamDrive -TeamDriveId "0AJ8Xjq3FcdCKUk9PVA" -Confirm:$false Removes the Team Drive '0AJ8Xjq3FcdCKUk9PVA', skipping confirmation #> [cmdletbinding(SupportsShouldProcess=$true,ConfirmImpact="High")] Param ( [parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)] [Alias('Id')] [String[]] $TeamDriveId, [parameter(Mandatory = $false,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias('Owner','PrimaryEmail','UserKey','Mail')] [string] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/drive' ServiceType = 'Google.Apis.Drive.v3.DriveService' User = $User } $service = New-GoogleService @serviceParams } Process { try { foreach ($id in $TeamDriveId) { if ($PSCmdlet.ShouldProcess("Deleting Team Drive '$id' from user '$User'")) { Write-Verbose "Deleting Team Drive '$id' from user '$User'" $request = $service.Teamdrives.Delete($id) $request.Execute() Write-Verbose "Team Drive '$id' successfully deleted from user '$User'" } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Remove-GSTeamDrive' function Set-GSDocContent { <# .SYNOPSIS Sets the content of a Google Doc. This overwrites any existing content on the Doc .DESCRIPTION Sets the content of a Google Doc. This overwrites any existing content on the Doc .PARAMETER FileID The unique Id of the file to set content on .PARAMETER Value The content to set .PARAMETER User The email or unique Id of the owner of the Drive file Defaults to the AdminEmail user .EXAMPLE $logStrings | Set-GSDocContent -FileId '1rhsAYTOB_vrpvfwImPmWy0TcVa2sgmQa_9u976' Sets the content of the specified Google Doc to the strings in the $logStrings variable. Any existing content on the doc will be overwritten. #> [CmdLetBinding()] Param ( [parameter(Mandatory = $true,Position = 0)] [String] $FileID, [parameter(Mandatory = $true,ValueFromPipeline = $true)] [String[]] $Value, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias('Owner','PrimaryEmail','UserKey','Mail')] [string] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/drive' ServiceType = 'Google.Apis.Drive.v3.DriveService' User = $User } $service = New-GoogleService @serviceParams $stream = New-Object 'System.IO.MemoryStream' $writer = New-Object 'System.IO.StreamWriter' $stream $concatStrings = @() } Process { foreach ($string in $Value) { $concatStrings += $string } } End { try { $concatStrings = $concatStrings -join "`n" $writer.Write($concatStrings) $writer.Flush() $contentType = 'text/plain' $body = New-Object 'Google.Apis.Drive.v3.Data.File' $request = $service.Files.Update($body,$FileId,$stream,$contentType) $request.QuotaUser = $User $request.ChunkSize = 512KB $request.SupportsTeamDrives = $true Write-Verbose "Setting content for File '$FileID'" $request.Upload() | Out-Null $stream.Close() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Set-GSDocContent' function Start-GSDriveFileUpload { <# .SYNOPSIS Starts uploading a file or list of files to Drive asynchronously .DESCRIPTION Starts uploading a file or list of files to Drive asynchronously. Allows full folder structure uploads by passing a folder as -Path and including the -Recurse parameter .PARAMETER Path The path of the file or folder to upload .PARAMETER Name The new name of the file once uploaded Defaults to the existing name of the file or folder .PARAMETER Description The description of the file or folder in Drive .PARAMETER Parents The unique Id of the parent folder in Drive to upload the file to Defaults to the root folder in My Drive .PARAMETER Recurse If $true and there is a Directory passed to -Path, this will rebuild the folder structure in Drive under the Parent Id and upload the files within accordingly .PARAMETER Wait If $true, waits for all uploads to complete and shows progress around the total upload .PARAMETER RetryCount How many times uploads should be retried when using the -Wait parameter Defaults to 10 .PARAMETER ThrottleLimit The limit of files to upload per batch while waiting .PARAMETER User The email or unique Id of the user to upload the files for .EXAMPLE Start-GSDriveFileUpload -Path "C:\Scripts","C:\Modules" -Recurse -Wait Starts uploading the Scripts and Modules folders and the files within them and waits for the uploads to complete, showing progress as files are uploaded #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias('FullName')] [ValidateScript( {Test-Path $_})] [String[]] $Path, [parameter(Mandatory = $false)] [String] $Name, [parameter(Mandatory = $false)] [String] $Description, [parameter(Mandatory = $false)] [String[]] $Parents, [parameter(Mandatory = $false)] [Switch] $Recurse, [parameter(Mandatory = $false)] [Switch] $Wait, [parameter(Mandatory = $false)] [Int] $RetryCount = 10, [parameter(Mandatory = $false)] [ValidateRange(1,1000)] [Int] $ThrottleLimit = 20, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias('Owner','PrimaryEmail','UserKey','Mail')] [string] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/drive' ServiceType = 'Google.Apis.Drive.v3.DriveService' User = $User } $service = New-GoogleService @serviceParams $taskList = [System.Collections.ArrayList]@() $fullTaskList = [System.Collections.ArrayList]@() $start = Get-Date $folIdHash = @{} $throttleCount = 0 $totalThrottleCount = 0 $totalFiles = 0 } Process { try { foreach ($file in $Path) { $details = Get-Item $file if ($details.PSIsContainer) { $newFolPerms = @{ Name = $details.Name User = $User Type = 'DriveFolder' Verbose = $false } if ($PSBoundParameters.Keys -contains 'Parents') { $newFolPerms['Parents'] = $PSBoundParameters['Parents'] } Write-Verbose "Creating new Drive folder '$($details.Name)'" $id = New-GSDriveFile @newFolPerms | Select-Object -ExpandProperty Id $folIdHash[$details.FullName.TrimEnd('\').TrimEnd('/')] = $id if ($Recurse) { $recurseList = Get-ChildItem $details.FullName -Recurse $recDirs = $recurseList | Where-Object {$_.PSIsContainer} | Sort-Object FullName if ($recDirs) { Write-Verbose "Creating recursive folder structure under '$($details.Name)'" $recDirs | ForEach-Object { $parPath = "$(Split-Path $_.FullName -Parent)" $newFolPerms = @{ Name = $_.Name User = $User Type = 'DriveFolder' Parents = [String[]]$folIdHash[$parPath] Verbose = $false } $id = New-GSDriveFile @newFolPerms | Select-Object -ExpandProperty Id $folIdHash[$_.FullName] = $id } } $details = $recurseList | Where-Object {!$_.PSIsContainer} | Sort-Object FullName $checkFolIdHash = $true $totalFiles = [int]$totalFiles + $details.Count } } else { $totalFiles++ $checkFolIdHash = $false } foreach ($detPart in $details) { $throttleCount++ $contentType = Get-MimeType $detPart $body = New-Object 'Google.Apis.Drive.v3.Data.File' -Property @{ Name = [String]$detPart.Name } if (!$checkFolIdHash -and ($PSBoundParameters.Keys -contains 'Parents')) { if ($Parents) { $body.Parents = [String[]]$Parents } } elseif ($checkFolIdHash) { $parPath = "$(Split-Path $detPart.FullName -Parent)" $body.Parents = [String[]]$folIdHash[$parPath] } if ($Description) { $body.Description = $Description } $stream = New-Object 'System.IO.FileStream' $detPart.FullName,'Open','Read' $request = $service.Files.Create($body,$stream,$contentType) $request.QuotaUser = $User $request.SupportsTeamDrives = $true $request.ChunkSize = 512KB $upload = $request.UploadAsync() $task = $upload.ContinueWith([System.Action[System.Threading.Tasks.Task]] {$stream.Dispose()}) Write-Verbose "[$($detPart.Name)] Upload Id $($upload.Id) has started" if (!$Script:DriveUploadTasks) { $Script:DriveUploadTasks = [System.Collections.ArrayList]@() } $script:DriveUploadTasks += [PSCustomObject]@{ Id = $upload.Id File = $detPart Length = $detPart.Length SizeInMB = [Math]::Round(($detPart.Length / 1MB),2,[MidPointRounding]::AwayFromZero) StartTime = $(Get-Date) Parents = $body.Parents User = $User Upload = $upload Request = $request } $taskList += [PSCustomObject]@{ Id = $upload.Id File = $detPart SizeInMB = [Math]::Round(($detPart.Length / 1MB),2,[MidPointRounding]::AwayFromZero) User = $User } $fullTaskList += [PSCustomObject]@{ Id = $upload.Id File = $detPart SizeInMB = [Math]::Round(($detPart.Length / 1MB),2,[MidPointRounding]::AwayFromZero) User = $User } if ($throttleCount -ge $ThrottleLimit) { $totalThrottleCount += $throttleCount if ($Wait) { Watch-GSDriveUpload -Id $taskList.Id -CountUploaded $totalThrottleCount -TotalUploading $totalFiles $throttleCount = 0 $taskList = [System.Collections.ArrayList]@() } } } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } End { if (!$Wait) { $fullTaskList } else { Watch-GSDriveUpload -Id $fullTaskList.Id -CountUploaded $totalFiles -TotalUploading $totalFiles $fullStatusList = Get-GSDriveFileUploadStatus -Id $fullTaskList.Id $failedFiles = $fullStatusList | Where-Object {$_.Status -eq "Failed"} if (!$failedFiles) { Write-Verbose "All files uploaded to Google Drive successfully! Total time: $("{0:c}" -f ((Get-Date) - $start) -replace "\..*")" } elseif ($RetryCount) { $totalRetries = 0 do { $throttleCount = 0 $totalThrottleCount = 0 $taskList = [System.Collections.ArrayList]@() $fullTaskList = [System.Collections.ArrayList]@() $details = Get-Item $failedFiles.File $totalFiles = [int]$totalFiles + $details.Count $totalRetries++ Write-Verbose "~ ~ ~ RETRYING [$totalFiles] FAILED FILES [Retry # $totalRetries / $RetryCount] ~ ~ ~" $details = Get-Item $failedFiles.File foreach ($detPart in $details) { $throttleCount++ $contentType = Get-MimeType $detPart $body = New-Object 'Google.Apis.Drive.v3.Data.File' -Property @{ Name = [String]$detPart.Name } $parPath = "$(Split-Path $detPart.FullName -Parent)" $body.Parents = [String[]]$folIdHash[$parPath] if ($Description) { $body.Description = $Description } $stream = New-Object 'System.IO.FileStream' $detPart.FullName,'Open','Read' $request = $service.Files.Create($body,$stream,$contentType) $request.QuotaUser = $User $request.SupportsTeamDrives = $true $request.ChunkSize = 512KB $upload = $request.UploadAsync() $task = $upload.ContinueWith([System.Action[System.Threading.Tasks.Task]] {$stream.Dispose()}) Write-Verbose "[$($detPart.Name)] Upload Id $($upload.Id) has started" if (!$Script:DriveUploadTasks) { $Script:DriveUploadTasks = [System.Collections.ArrayList]@() } $script:DriveUploadTasks += [PSCustomObject]@{ Id = $upload.Id File = $detPart Length = $detPart.Length SizeInMB = [Math]::Round(($detPart.Length / 1MB),2,[MidPointRounding]::AwayFromZero) StartTime = $(Get-Date) Parents = $body.Parents User = $User Upload = $upload Request = $request } $taskList += [PSCustomObject]@{ Id = $upload.Id File = $detPart SizeInMB = [Math]::Round(($detPart.Length / 1MB),2,[MidPointRounding]::AwayFromZero) User = $User } $fullTaskList += [PSCustomObject]@{ Id = $upload.Id File = $detPart SizeInMB = [Math]::Round(($detPart.Length / 1MB),2,[MidPointRounding]::AwayFromZero) User = $User } if ($throttleCount -ge $ThrottleLimit) { $totalThrottleCount += $throttleCount if ($Wait) { Watch-GSDriveUpload -Id $taskList.Id -CountUploaded $totalThrottleCount -TotalUploading $totalFiles -Action Retrying $throttleCount = 0 $taskList = [System.Collections.ArrayList]@() } } } Watch-GSDriveUpload -Id $fullTaskList.Id -Action Retrying -CountUploaded $totalFiles -TotalUploading $totalFiles $fullStatusList = Get-GSDriveFileUploadStatus -Id $fullTaskList.Id $failedFiles = $fullStatusList | Where-Object {$_.Status -eq "Failed"} } until (!$failedFiles -or ($totalRetries -ge $RetryCount)) if ($failedFiles) { Write-Warning "The following files failed to upload:`n`n$($failedFiles | Select-Object Id,Status,Exception,File | Format-List | Out-String)" } elseif (!$failedFiles) { Write-Verbose "All files uploaded to Google Drive successfully! Total time: $("{0:c}" -f ((Get-Date) - $start) -replace "\..*")" } } [Console]::CursorVisible = $true } } } Export-ModuleMember -Function 'Start-GSDriveFileUpload' function Update-GSDriveFile { <# .SYNOPSIS Updates the metadata for a Drive file .DESCRIPTION Updates the metadata for a Drive file .PARAMETER FileId The unique Id of the Drive file to Update .PARAMETER Path The path to the local file whose content you would like to upload to Drive. .PARAMETER Name The new name of the Drive file .PARAMETER Description The new description of the Drive file .PARAMETER AddParents The parent Ids to add .PARAMETER RemoveParents The parent Ids to remove .PARAMETER Projection The defined subset of fields to be returned Available values are: * "Minimal" * "Standard" * "Full" * "Access" .PARAMETER Fields The specific fields to returned .PARAMETER User The email or unique Id of the Drive file owner .EXAMPLE Update-GSDriveFile -FileId '1rhsAYTOB_vrpvfwImPmWy0TcVa2sgmQa_9u976' -Name "To-Do Progress" Updates the Drive file with a new name, "To-Do Progress" .EXAMPLE Update-GSDriveFile -FileId '1rhsAYTOB_vrpvfwImPmWy0TcVa2sgmQa_9u976' -Path "C:\Pics\NewPic.png" Updates the Drive file with the content of the file at that path. In this example, the Drive file is a PNG named "Test.png". This will change the content of the file in Drive to match NewPic.png as well as rename it to "NewPic.png" #> [cmdletbinding(DefaultParameterSetName = "Depth")] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias('Id')] [String] $FileId, [parameter(Mandatory = $false,Position = 1)] [ValidateScript({Test-Path $_})] [String] $Path, [parameter(Mandatory = $false)] [String] $Name, [parameter(Mandatory = $false)] [String] $Description, [parameter(Mandatory = $false)] [String[]] $AddParents, [parameter(Mandatory = $false)] [String[]] $RemoveParents, [parameter(Mandatory = $false,ParameterSetName = "Depth")] [Alias('Depth')] [ValidateSet("Minimal","Standard","Full","Access")] [String] $Projection = "Full", [parameter(Mandatory = $false,ParameterSetName = "Fields")] [ValidateSet("appProperties","capabilities","contentHints","createdTime","description","explicitlyTrashed","fileExtension","folderColorRgb","fullFileExtension","hasThumbnail","headRevisionId","iconLink","id","imageMediaMetadata","isAppAuthorized","kind","lastModifyingUser","md5Checksum","mimeType","modifiedByMe","modifiedByMeTime","modifiedTime","name","originalFilename","ownedByMe","owners","parents","permissions","properties","quotaBytesUsed","shared","sharedWithMeTime","sharingUser","size","spaces","starred","thumbnailLink","thumbnailVersion","trashed","version","videoMediaMetadata","viewedByMe","viewedByMeTime","viewersCanCopyContent","webContentLink","webViewLink","writersCanShare")] [String[]] $Fields, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias('Owner','PrimaryEmail','UserKey','Mail')] [string] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($Projection) { $fs = switch ($Projection) { Standard { @("createdTime","description","fileExtension","id","lastModifyingUser","modifiedTime","name","owners","parents","properties","version","webContentLink","webViewLink") } Access { @("createdTime","description","fileExtension","id","lastModifyingUser","modifiedTime","name","ownedByMe","owners","parents","permissionIds","permissions","shared","sharedWithMeTime","sharingUser","viewedByMe","viewedByMeTime","viewersCanCopyContent","writersCanShare") } Full { @("appProperties","capabilities","contentHints","createdTime","description","explicitlyTrashed","fileExtension","folderColorRgb","fullFileExtension","hasAugmentedPermissions","hasThumbnail","headRevisionId","iconLink","id","imageMediaMetadata","isAppAuthorized","kind","lastModifyingUser","md5Checksum","mimeType","modifiedByMe","modifiedByMeTime","modifiedTime","name","originalFilename","ownedByMe","owners","parents","permissionIds","permissions","properties","quotaBytesUsed","shared","sharedWithMeTime","sharingUser","size","spaces","starred","teamDriveId","thumbnailLink","thumbnailVersion","trashed","trashedTime","trashingUser","version","videoMediaMetadata","viewedByMe","viewedByMeTime","viewersCanCopyContent","webContentLink","webViewLink","writersCanShare") } } } elseif ($Fields) { $fs = $Fields } if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/drive' ServiceType = 'Google.Apis.Drive.v3.DriveService' User = $User } $service = New-GoogleService @serviceParams } Process { try { $body = New-Object 'Google.Apis.Drive.v3.Data.File' if ($Name) { $body.Name = [String]$Name } if ($Description) { $body.Description = $Description } if ($PSBoundParameters.Keys -contains 'Path') { $ioFile = Get-Item $Path $contentType = Get-MimeType $ioFile if ($PSBoundParameters.Keys -notcontains 'Name') { $body.Name = $ioFile.Name } $stream = New-Object 'System.IO.FileStream' $ioFile.FullName,'Open','Read' $request = $service.Files.Update($body,$FileId,$stream,$contentType) $request.QuotaUser = $User $request.ChunkSize = 512KB } else { $request = $service.Files.Update($body,$FileId) } $request.SupportsTeamDrives = $true if ($fs) { $request.Fields = $($fs -join ",") } if ($AddParents) { $request.AddParents = $($AddParents -join ",") } if ($RemoveParents) { $request.RemoveParents = $($RemoveParents -join ",") } Write-Verbose "Updating file '$FileId' for user '$User'" if ($PSBoundParameters.Keys -contains 'Path') { $request.Upload() | Out-Null $stream.Close() } else { $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Update-GSDriveFile' function Update-GSTeamDrive { <# .SYNOPSIS Update metatdata for a Team Drive .DESCRIPTION Update metatdata for a Team Drive .PARAMETER TeamDriveId The unique Id of the Team Drive to update .PARAMETER User The user to create the Team Drive for (must have permissions to create Team Drives) .PARAMETER Name The name of the Team Drive .PARAMETER CanAddChildren Whether the current user can add children to folders in this Team Drive .PARAMETER CanChangeTeamDriveBackground Whether the current user can change the background of this Team Drive .PARAMETER CanComment Whether the current user can comment on files in this Team Drive .PARAMETER CanCopy Whether the current user can copy files in this Team Drive .PARAMETER CanDeleteTeamDrive Whether the current user can delete this Team Drive. Attempting to delete the Team Drive may still fail if there are untrashed items inside the Team Drive .PARAMETER CanDownload Whether the current user can download files in this Team Drive .PARAMETER CanEdit Whether the current user can edit files in this Team Drive .PARAMETER CanListChildren Whether the current user can list the children of folders in this Team Drive .PARAMETER CanManageMembers Whether the current user can add members to this Team Drive or remove them or change their role .PARAMETER CanReadRevisions Whether the current user can read the revisions resource of files in this Team Drive .PARAMETER CanRemoveChildren Whether the current user can remove children from folders in this Team Drive .PARAMETER CanRename Whether the current user can rename files or folders in this Team Drive .PARAMETER CanRenameTeamDrive Whether the current user can rename this Team Drive .PARAMETER CanShare Whether the current user can share files or folders in this Team Drive .EXAMPLE Update-GSTeamDrive -TeamDriveId '0AJ8Xjq3FcdCKUk9PVA' -Name "HR Document Repo" Updated the Team Drive with a new name, "HR Document Repo" #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0)] [String] $TeamDriveId, [parameter(Mandatory = $false,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias('Owner','PrimaryEmail','UserKey','Mail')] [string] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false)] [String] $Name, [parameter(Mandatory = $false)] [Switch] $CanAddChildren, [parameter(Mandatory = $false)] [Switch] $CanChangeTeamDriveBackground, [parameter(Mandatory = $false)] [Switch] $CanComment, [parameter(Mandatory = $false)] [Switch] $CanCopy, [parameter(Mandatory = $false)] [Switch] $CanDeleteTeamDrive, [parameter(Mandatory = $false)] [Switch] $CanDownload, [parameter(Mandatory = $false)] [Switch] $CanEdit, [parameter(Mandatory = $false)] [Switch] $CanListChildren, [parameter(Mandatory = $false)] [Switch] $CanManageMembers, [parameter(Mandatory = $false)] [Switch] $CanReadRevisions, [parameter(Mandatory = $false)] [Switch] $CanRemoveChildren, [parameter(Mandatory = $false)] [Switch] $CanRename, [parameter(Mandatory = $false)] [Switch] $CanRenameTeamDrive, [parameter(Mandatory = $false)] [Switch] $CanShare ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/drive' ServiceType = 'Google.Apis.Drive.v3.DriveService' User = $User } $service = New-GoogleService @serviceParams } Process { try { $body = New-Object 'Google.Apis.Drive.v3.Data.TeamDrive' $capabilities = New-Object 'Google.Apis.Drive.v3.Data.TeamDrive+CapabilitiesData' foreach ($key in $PSBoundParameters.Keys) { switch ($key) { Default { if ($capabilities.PSObject.Properties.Name -contains $key) { $capabilities.$key = $PSBoundParameters[$key] } elseif ($body.PSObject.Properties.Name -contains $key) { $body.$key = $PSBoundParameters[$key] } } } } $body.Capabilities = $capabilities $request = $service.Teamdrives.Update($body,$TeamDriveId) Write-Verbose "Updating Team Drive '$Name' for user '$User'" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Update-GSTeamDrive' function Watch-GSDriveUpload { <# .SYNOPSIS Shows progress in the console of current Drive file uploads .DESCRIPTION Shows progress in the console of current Drive file uploads .PARAMETER Id The upload Id(s) that you would like to watch .PARAMETER Action Whether the action is uploading or retrying. This is mainly for use in Start-GSDriveFileUpload and defaults to 'Uploading' .PARAMETER CountUploaded Current file count being uploaded .PARAMETER TotalUploading Total file count being uploaded .EXAMPLE Watch-GSDriveUpload Watches the files currently being uploaded from the active session #> [CmdletBinding()] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Int[]] $Id, [parameter(Mandatory = $false)] [ValidateSet('Uploading','Retrying')] [String] $Action = "Uploading", [parameter(Mandatory = $false)] [Int] $CountUploaded, [parameter(Mandatory = $false)] [Int] $TotalUploading ) Begin { Write-Verbose "Watching Drive File Upload" } Process { do { $i = 1 if ($PSBoundParameters.Keys -contains 'Id') { $statusList = Get-GSDriveFileUploadStatus -Verbose:$false } else { $statusList = Get-GSDriveFileUploadStatus -Id @($Id) -Verbose:$false } if ($statusList) { $totalPercent = 0 $totalSecondsRemaining = 0 $count = 0 $statusList | ForEach-Object { $count++ $totalPercent += $_.PercentComplete $totalSecondsRemaining += $_.Remaining.TotalSeconds } $curCount = if ($PSBoundParameters.Keys -contains 'CountUploaded') { $CountUploaded } else { $count } $totalCount = if ($PSBoundParameters.Keys -contains 'TotalUploading') { $TotalUploading } else { $count } $totalPercent = $totalPercent / $totalCount $totalSecondsRemaining = $totalSecondsRemaining / $totalCount $parentParams = @{ Activity = "[$([Math]::Round($totalPercent,4))%] $Action [$curCount / $totalCount] files to Google Drive" SecondsRemaining = $($statusList.Remaining.TotalSeconds | Sort-Object | Select-Object -Last 1) } if (!($statusList | Where-Object {$_.Status -ne "Completed"})) { $parentParams['Completed'] = $true } else { $parentParams['PercentComplete'] = [Math]::Round($totalPercent,4) } if ($psEditor -or $IsMacOS -or $IsLinux) { Write-InlineProgress @parentParams } else { $parentParams['Id'] = 1 Write-Progress @parentParams } if (!$psEditor -and !$IsMacOS -and !$IsLinux -and ($statusList.Count -le 5)) { foreach ($status in $statusList) { $i++ $statusFmt = if ($status.Status -eq "Completed") { "Completed uploading" } else { $status.Status } $progParams = @{ Activity = "[$($status.PercentComplete)%] [ID: $($status.Id)] $($statusFmt) file '$($status.File)' to Google Drive$(if($status.Parents){" (Parents: '$($status.Parents -join "', '")')"})" SecondsRemaining = $status.Remaining.TotalSeconds Id = $i ParentId = 1 } if ($_.Status -eq "Completed") { $progParams['Completed'] = $true } else { $progParams['PercentComplete'] = [Math]::Round($status.PercentComplete,4) } Write-Progress @progParams } } Start-Sleep -Seconds 1 } } until (!$statusList -or !($statusList | Where-Object {$_.Status -notin @("Failed","Completed")})) } } Export-ModuleMember -Function 'Watch-GSDriveUpload' function Add-GSGmailFilter { <# .SYNOPSIS Adds a new Gmail filter .DESCRIPTION Adds a new Gmail filter .PARAMETER User The email of the user you are adding the filter for .PARAMETER From The sender's display name or email address. .PARAMETER To The recipient's display name or email address. Includes recipients in the "to", "cc", and "bcc" header fields. You can use simply the local part of the email address. For example, "example" and "example@" both match "example@gmail.com". This field is case-insensitive .PARAMETER Subject Case-insensitive phrase found in the message's subject. Trailing and leading whitespace are be trimmed and adjacent spaces are collapsed .PARAMETER Query Only return messages matching the specified query. Supports the same query format as the Gmail search box. For example, "from:someuser@example.com rfc822msgid: is:unread" .PARAMETER NegatedQuery Only return messages not matching the specified query. Supports the same query format as the Gmail search box. For example, "from:someuser@example.com rfc822msgid: is:unread" .PARAMETER HasAttachment Whether the message has any attachment .PARAMETER ExcludeChats Whether the response should exclude chats .PARAMETER AddLabelIDs List of labels to add to the message .PARAMETER RemoveLabelIDs List of labels to remove from the message .PARAMETER Forward Email address that the message should be forwarded to .PARAMETER Size The size of the entire RFC822 message in bytes, including all headers and attachments .PARAMETER SizeComparison How the message size in bytes should be in relation to the size field. Acceptable values are: * "larger" * "smaller" * "unspecified" .PARAMETER Raw If $true, returns the raw response. If not passed or -Raw:$false, response is formatted as a flat object for readability .EXAMPLE Add-GSGmailFilter -To admin@domain.com -ExcludeChats -Forward "admin_directMail@domain.com" Adds a filter for the AdminEmail user to forward all mail sent directly to the to "admin_directMail@domain.com" #> [cmdletbinding()] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [string] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [string] $From, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [string] $To, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [string] $Subject, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [string] $Query, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [string] $NegatedQuery, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Switch] $HasAttachment, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Switch] $ExcludeChats, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [string[]] $AddLabelIDs, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [string[]] $RemoveLabelIDs, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [string] $Forward, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [int] $Size, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [ValidateSet('Larger','Smaller','Unspecified')] [string] $SizeComparison, [parameter(Mandatory = $false)] [Switch] $Raw ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/gmail.settings.basic' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $User } $service = New-GoogleService @serviceParams } Process { try { $body = New-Object 'Google.Apis.Gmail.v1.Data.Filter' $action = New-Object 'Google.Apis.Gmail.v1.Data.FilterAction' $criteria = New-Object 'Google.Apis.Gmail.v1.Data.FilterCriteria' foreach ($key in $PSBoundParameters.Keys) { switch ($key) { AddLabelIDs { $action.$key = [String[]]($PSBoundParameters[$key]) } RemoveLabelIds { $action.$key = [String[]]($PSBoundParameters[$key]) } Forward { $action.$key = $PSBoundParameters[$key] } Default { if ($criteria.PSObject.Properties.Name -contains $key) { $criteria.$key = $PSBoundParameters[$key] } } } } $body.Action = $action $body.Criteria = $criteria $request = $service.Users.Settings.Filters.Create($body,$User) Write-Verbose "Creating Filter for user '$User'" $response = $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru if (!$Raw) { $response = $response | Select-Object User,Id,@{N = "From";E = {$_.criteria.from}},@{N = "To";E = {$_.criteria.to}},@{N = "Subject";E = {$_.criteria.subject}},@{N = "Query";E = {$_.criteria.query}},@{N = "NegatedQuery";E = {$_.criteria.negatedQuery}},@{N = "HasAttachment";E = {$_.criteria.hasAttachment}},@{N = "ExcludeChats";E = {$_.criteria.excludeChats}},@{N = "Size";E = {$_.criteria.size}},@{N = "SizeComparison";E = {$_.criteria.sizeComparison}},@{N = "AddLabelIds";E = {$_.action.addLabelIds}},@{N = "RemoveLabelIds";E = {$_.action.removeLabelIds}},@{N = "Forward";E = {$_.action.forward}} } $response } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Add-GSGmailFilter' function Add-GSGmailForwardingAddress { <# .SYNOPSIS Creates a forwarding address. .DESCRIPTION Creates a forwarding address. If ownership verification is required, a message will be sent to the recipient and the resource's verification status will be set to pending; otherwise, the resource will be created with verification status set to accepted. .PARAMETER ForwardingAddress An email address to which messages can be forwarded. .PARAMETER User The user to create the forwarding addresses for Defaults to the AdminEmail user .EXAMPLE Add-GSGmailForwardingAddress "joe@domain.com" Adds joe@domain.com as a forwarding address for the AdminEmail user #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias("Id")] [string[]] $ForwardingAddress, [parameter(Mandatory = $false,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [string] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/gmail.settings.sharing' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $User } $service = New-GoogleService @serviceParams } Process { try { foreach ($fwd in $ForwardingAddress) { $body = New-Object 'Google.Apis.Gmail.v1.Data.ForwardingAddress' -Property @{ ForwardingEmail = $fwd } $request = $service.Users.Settings.ForwardingAddresses.Create($body,$User) Write-Verbose "Creating Forwarding Address '$fwd' for user '$User'" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Add-GSGmailForwardingAddress' function Get-GSGmailAutoForwardingSettings { <# .SYNOPSIS Gets AutoForwarding settings .DESCRIPTION Gets AutoForwarding settings .PARAMETER User The user to get the AutoForwarding settings for Defaults to the AdminEmail user .EXAMPLE Get-GSGmailAutoForwardingSettings Gets the AutoForwarding settings for the AdminEmail user #> [cmdletbinding()] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [string] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/gmail.settings.basic' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $User } $service = New-GoogleService @serviceParams } Process { try { $request = $service.Users.Settings.GetAutoForwarding($User) Write-Verbose "Getting AutoForwarding settings for user '$User'" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSGmailAutoForwardingSettings' function Get-GSGmailFilter { <# .SYNOPSIS Gets Gmail filter details .DESCRIPTION Gets Gmail filter details .PARAMETER FilterId The unique Id of the filter you would like to retrieve information for. If excluded, all filters for the user are returned .PARAMETER User The email of the user you are getting the filter information for .PARAMETER Raw If $true, returns the raw response. If not passed or -Raw:$false, response is formatted as a flat object for readability .EXAMPLE Get-GSGmailFilter -User joe Gets the list of filters for Joe #> [cmdletbinding()] Param ( [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias("Id")] [string[]] $FilterId, [parameter(Mandatory = $false,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [string] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false)] [switch] $Raw ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/gmail.settings.basic' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $User } $service = New-GoogleService @serviceParams } Process { try { if ($FilterId) { foreach ($fil in $FilterId) { $request = $service.Users.Settings.Filters.Get($User,$fil) Write-Verbose "Getting Filter Id '$fil' for user '$User'" $response = $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru if (!$Raw) { $response = $response | Select-Object User,Id,@{N = "From";E = {$_.criteria.from}},@{N = "To";E = {$_.criteria.to}},@{N = "Subject";E = {$_.criteria.subject}},@{N = "Query";E = {$_.criteria.query}},@{N = "NegatedQuery";E = {$_.criteria.negatedQuery}},@{N = "HasAttachment";E = {$_.criteria.hasAttachment}},@{N = "ExcludeChats";E = {$_.criteria.excludeChats}},@{N = "Size";E = {$_.criteria.size}},@{N = "SizeComparison";E = {$_.criteria.sizeComparison}},@{N = "AddLabelIds";E = {$_.action.addLabelIds}},@{N = "RemoveLabelIds";E = {$_.action.removeLabelIds}},@{N = "Forward";E = {$_.action.forward}} } $response } } else { $request = $service.Users.Settings.Filters.List($User) Write-Verbose "Getting Filter List for user '$User'" $response = $request.Execute() | Select-Object -ExpandProperty Filter | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru if (!$Raw) { $response = $response | Select-Object User,Id,@{N = "From";E = {$_.criteria.from}},@{N = "To";E = {$_.criteria.to}},@{N = "Subject";E = {$_.criteria.subject}},@{N = "Query";E = {$_.criteria.query}},@{N = "NegatedQuery";E = {$_.criteria.negatedQuery}},@{N = "HasAttachment";E = {$_.criteria.hasAttachment}},@{N = "ExcludeChats";E = {$_.criteria.excludeChats}},@{N = "Size";E = {$_.criteria.size}},@{N = "SizeComparison";E = {$_.criteria.sizeComparison}},@{N = "AddLabelIds";E = {$_.action.addLabelIds}},@{N = "RemoveLabelIds";E = {$_.action.removeLabelIds}},@{N = "Forward";E = {$_.action.forward}} } $response } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSGmailFilter' function Get-GSGmailForwardingAddress { <# .SYNOPSIS Gets Gmail forwarding address information for the user .DESCRIPTION Gets Gmail forwarding address information for the user .PARAMETER ForwardingAddress The forwarding address you would like to get info for. If excluded, gets the list of forwarding addresses and their info for the user .PARAMETER User The user to get the forwarding addresses for Defaults to the AdminEmail user .EXAMPLE Get-GSGmailForwardingAddress Gets the list of forwarding addresses for the AdminEmail user #> [cmdletbinding()] Param ( [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias("Id")] [string[]] $ForwardingAddress, [parameter(Mandatory = $false,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [string] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/gmail.settings.basic' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $User } $service = New-GoogleService @serviceParams } Process { try { if ($ForwardingAddress) { foreach ($fwd in $ForwardingAddress) { $request = $service.Users.Settings.ForwardingAddresses.Get($User,$fwd) Write-Verbose "Getting Forwarding Address '$fwd' for user '$User'" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } } else { $request = $service.Users.Settings.ForwardingAddresses.List($User) Write-Verbose "Getting Forwarding Address List for user '$User'" $request.Execute() | Select-Object -ExpandProperty ForwardingAddresses | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSGmailForwardingAddress' function Get-GSGmailImapSettings { <# .SYNOPSIS Gets IMAP settings .DESCRIPTION Gets IMAP settings .PARAMETER User The user to get the IMAP settings for Defaults to the AdminEmail user .EXAMPLE Get-GSGmailImapSettings Gets the IMAP settings for the AdminEmail user #> [cmdletbinding()] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [string] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/gmail.settings.basic' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $User } $service = New-GoogleService @serviceParams } Process { try { $request = $service.Users.Settings.GetImap($User) Write-Verbose "Getting IMAP settings for user '$User'" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSGmailImapSettings' function Get-GSGmailLabel { <# .SYNOPSIS Gets Gmail label information for the user .DESCRIPTION Gets Gmail label information for the user .PARAMETER LabelId The unique Id of the label to get information for. If excluded, returns the list of labels for the user .PARAMETER User The user to get label information for Defaults to the AdminEmail user .EXAMPLE Get-GSGmailLabel Gets the Gmail labels of the AdminEmail user #> [cmdletbinding()] Param ( [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias("Id")] [string[]] $LabelId, [parameter(Mandatory = $false,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [string] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://mail.google.com' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $User } $service = New-GoogleService @serviceParams } Process { try { if ($LabelId) { foreach ($label in $LabelId) { $request = $service.Users.Labels.Get($User,$label) Write-Verbose "Getting Label Id '$label' for user '$User'" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } } else { $request = $service.Users.Labels.List($User) Write-Verbose "Getting Label List for user '$User'" $request.Execute() | Select-Object -ExpandProperty Labels | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSGmailLabel' function Get-GSGmailMessage { <# .SYNOPSIS Gets Gmail message details .DESCRIPTION Gets Gmail message details .PARAMETER User The primary email of the user who owns the message Defaults to the AdminEmail user .PARAMETER Id The Id of the message to retrieve info for .PARAMETER ParseMessage If $true, returns the parsed raw message .PARAMETER SaveAttachmentsTo If the message has attachments, the path to save the attachments to. If excluded, attachments are not saved locally .PARAMETER Format The format of the message metadata to retrieve Available values are: * "Full" * "Metadata" * "Minimal" * "Raw" Defaults to "Full", but forces -Format as "Raw" if -ParseMessage or -SaveAttachmentsTo are used .EXAMPLE Get-GSGmailMessage -Id 1615f9a6ee36cb5b -ParseMessage Gets the full message details for the provided Id and parses out the raw MIME message content #> [cmdletbinding(DefaultParameterSetName = "Format")] Param ( [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [string] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)] [Alias('MessageId')] [String[]] $Id, [parameter(Mandatory = $false,ParameterSetName = "ParseMessage")] [switch] $ParseMessage, [parameter(Mandatory = $false,ParameterSetName = "ParseMessage")] [Alias('AttachmentOutputPath','OutFilePath')] [ValidateScript({(Get-Item $_).PSIsContainer})] [string] $SaveAttachmentsTo, [parameter(Mandatory = $false,ParameterSetName = "Format")] [ValidateSet("Full","Metadata","Minimal","Raw")] [string] $Format = "Full" ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } if ($ParseMessage) { $Format = "Raw" } $serviceParams = @{ Scope = 'https://mail.google.com' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $User } $service = New-GoogleService @serviceParams } Process { try { foreach ($mId in $Id) { $request = $service.Users.Messages.Get($User,$mId) $request.Format = $Format foreach ($key in $PSBoundParameters.Keys | Where-Object {$_ -ne "Id"}) { switch ($key) { Default { if ($request.PSObject.Properties.Name -contains $key) { $request.$key = $PSBoundParameters[$key] } } } } Write-Verbose "Getting Message Id '$mId' for user '$User'" $result = $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru if ($ParseMessage) { $parsed = Read-MimeMessage -String $(Convert-Base64 -From WebSafeBase64String -To NormalString -String $result.Raw) | Select-Object @{N = 'User';E = {$User}},@{N = "Id";E = {$result.Id}},@{N = "ThreadId";E = {$result.ThreadId}},@{N = "LabelIds";E = {$result.LabelIds}},@{N = "Snippet";E = {$result.Snippet}},@{N = "HistoryId";E = {$result.HistoryId}},@{N = "InternalDate";E = {$result.InternalDate}},@{N = "InternalDateConverted";E = {Convert-EpochToDate -EpochString $result.internalDate}},@{N = "SizeEstimate";E = {$result.SizeEstimate}},* if ($SaveAttachmentsTo) { $resPath = Resolve-Path $SaveAttachmentsTo $attachments = $parsed.Attachments foreach ($att in $attachments) { $fileName = Join-Path $resPath $att.FileName Write-Verbose "Saving attachment to path '$fileName'" $stream = [System.IO.File]::Create($fileName) $att.ContentObject.DecodeTo($stream) $stream.Close() } } $parsed } else { $result } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSGmailMessage' function Get-GSGmailMessageList { <# .SYNOPSIS Gets a list of messages .DESCRIPTION Gets a list of messages .PARAMETER User The primary email of the user to list messages for Defaults to the AdminEmail user .PARAMETER Filter Only return messages matching the specified query. Supports the same query format as the Gmail search box. For example, "from:someuser@example.com rfc822msgid: is:unread" More info on Gmail search operators here: https://support.google.com/mail/answer/7190?hl=en .PARAMETER LabelIds Only return messages with labels that match all of the specified label IDs .PARAMETER ExcludeChats Exclude chats from the message list .PARAMETER IncludeSpamTrash Include messages from SPAM and TRASH in the results .PARAMETER PageSize The page size of the result set .EXAMPLE Get-GSGmailMessageList -Filter "to:me","after:2017/12/25" -ExcludeChats Gets the list of messages sent directly to the user after 2017/12/25 excluding chats #> [cmdletbinding()] Param ( [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [String] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false)] [Alias('Query')] [String[]] $Filter, [parameter(Mandatory = $false)] [Alias('LabelId')] [String[]] $LabelIds, [parameter(Mandatory = $false)] [switch] $ExcludeChats, [parameter(Mandatory = $false)] [switch] $IncludeSpamTrash, [parameter(Mandatory = $false)] [ValidateRange(1,500)] [Int] $PageSize = "500" ) Process { try { foreach ($U in $User) { if ($U -ceq 'me') { $U = $Script:PSGSuite.AdminEmail } elseif ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://mail.google.com' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $U } if ($ExcludeChats) { if ($Filter) { $Filter += "-in:chats" } else { $Filter = "-in:chats" } } $service = New-GoogleService @serviceParams $request = $service.Users.Messages.List($U) foreach ($key in $PSBoundParameters.Keys) { switch ($key) { Filter { $request.Q = $($Filter -join " ") } LabelIds { $request.LabelIds = [String[]]$LabelIds } Default { if ($request.PSObject.Properties.Name -contains $key) { $request.$key = $PSBoundParameters[$key] } } } } if ($PageSize) { $request.MaxResults = $PageSize } if ($Filter) { Write-Verbose "Getting all Messages matching filter '$Filter' for user '$U'" } else { Write-Verbose "Getting all Messages for user '$U'" } [int]$i = 1 do { $result = $request.Execute() if ($result.Messages) { $result.Messages | Add-Member -MemberType NoteProperty -Name 'User' -Value $U -PassThru | Add-Member -MemberType NoteProperty -Name 'Filter' -Value $Filter -PassThru } if ($result.NextPageToken) { $request.PageToken = $result.NextPageToken } [int]$retrieved = ($i + $result.Messages.Count) - 1 Write-Verbose "Retrieved $retrieved Messages..." [int]$i = $i + $result.Messages.Count } until (!$result.NextPageToken) } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSGmailMessageList' function Get-GSGmailPopSettings { <# .SYNOPSIS Gets POP settings .DESCRIPTION Gets POP settings .PARAMETER User The user to get the POP settings for Defaults to the AdminEmail user .EXAMPLE Get-GSGmailPopSettings Gets the POP settings for the AdminEmail user #> [cmdletbinding()] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [string] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/gmail.settings.basic' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $User } $service = New-GoogleService @serviceParams } Process { try { $request = $service.Users.Settings.GetPop($User) Write-Verbose "Getting POP settings for user '$User'" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSGmailPopSettings' function Get-GSGmailProfile { <# .SYNOPSIS Gets Gmail profile for the user .DESCRIPTION Gets Gmail profile for the user .PARAMETER User The user to get profile of Defaults to the AdminEmail user .EXAMPLE Get-GSGmailProfile Gets the Gmail profile of the AdminEmail user #> [cmdletbinding()] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String[]] $User = $Script:PSGSuite.AdminEmail ) Process { foreach ($U in $User) { try { if ($U -ceq 'me') { $U = $Script:PSGSuite.AdminEmail } elseif ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://mail.google.com' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $U } $service = New-GoogleService @serviceParams $request = $service.Users.GetProfile($U) Write-Verbose "Getting Gmail profile for user '$U'" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $U -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Get-GSGmailProfile' function Get-GSGmailSMIMEInfo { <# .SYNOPSIS Gets Gmail S/MIME info .DESCRIPTION Gets Gmail S/MIME info .PARAMETER SendAsEmail The email address that appears in the "From:" header for mail sent using this alias. .PARAMETER Id The immutable ID for the SmimeInfo. If left blank, returns the list of S/MIME infos for the SendAsEmail and User .PARAMETER User The user's email address Defaults to the AdminEmail user .EXAMPLE Get-GSGmailSMIMEInfo -SendAsEmail 'joe@otherdomain.com' -User joe@domain.com Gets the list of S/MIME infos for Joe's SendAsEmail 'joe@otherdomain.com' #> [cmdletbinding()] Param ( [parameter(Mandatory = $true)] [string] $SendAsEmail, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [string[]] $Id, [parameter(Mandatory = $false)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [string] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/gmail.settings.basic' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $User } $service = New-GoogleService @serviceParams } Process { try { if ($PSBoundParameters.Keys -contains 'Id') { foreach ($I in $Id) { $request = $service.Users.Settings.SendAs.SmimeInfo.Get($User,$SendAsEmail,$I) Write-Verbose "Getting S/MIME Id '$I' of SendAsEmail '$SendAsEmail' for user '$User'" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } } else { $request = $service.Users.Settings.SendAs.SmimeInfo.List($User,$SendAsEmail) Write-Verbose "Getting list of S/MIME Id's of SendAsEmail '$SendAsEmail' for user '$User'" $request.Execute() | Select-Object -ExpandProperty smimeInfo | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSGmailSMIMEInfo' function Get-GSGmailVacationSettings { <# .SYNOPSIS Gets Vacation settings .DESCRIPTION Gets Vacation settings .PARAMETER User The user to get the Vacation settings for Defaults to the AdminEmail user .EXAMPLE Get-GSGmailVacationSettings Gets the Vacation settings for the AdminEmail user #> [cmdletbinding()] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [string] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/gmail.settings.basic' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $User } $service = New-GoogleService @serviceParams } Process { try { $request = $service.Users.Settings.GetVacation($User) Write-Verbose "Getting Vacation settings for user '$User'" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSGmailVacationSettings' function New-GSGmailLabel { <# .SYNOPSIS Adds a Gmail label .DESCRIPTION Adds a Gmail label .PARAMETER Name The name of the label to add .PARAMETER LabelListVisibility The visibility of the label in the label list in the Gmail web interface. Acceptable values are: * "labelHide": Do not show the label in the label list. * "labelShow": Show the label in the label list. (Default) * "labelShowIfUnread": Show the label if there are any unread messages with that label. .PARAMETER MessageListVisibility The visibility of messages with this label in the message list in the Gmail web interface. Acceptable values are: * "hide": Do not show the label in the message list. * "show": Show the label in the message list. (Default) .PARAMETER BackgroundColor The background color of the label Options and their corresponding hex code: Amethyst = '#8e63ce' BananaMania = '#fce8b3' Bermuda = '#68dfa9' BilobaFlower = '#b694e8' Black = '#000000' BlueRomance = '#c6f3de' BrandyPunch = '#cf8933' BurntSienna = '#e66550' Cadillac = '#b65775' Camelot = '#83334c' CeruleanBlue = '#285bac' ChathamsBlue = '#1c4587' Concrete = '#f3f3f3' CornflowerBlue = '#4a86e8' LightCornflowerBlue = '#6d9eeb' CreamCan = '#f2c960' Cupid = '#fbc8d9' DeepBlush = '#e07798' Desert = '#a46a21' DoveGray = '#666666' DustyGray = '#999999' Eucalyptus = '#2a9c68' Flesh = '#ffd6a2' FringyFlower = '#b9e4d0' Gallery = '#efefef' Goldenrod = '#fad165' Illusion = '#f7a7c0' Jewel = '#1a764d' Koromiko = '#ffbc6b' LightMountainMeadow = '#16a766' LightShamrock = '#43d692' LuxorGold = '#aa8831' MandysPink = '#f6c5be' MediumPurple = '#a479e2' Meteorite = '#41236d' MoonRaker = '#d0bcf1' LightMoonRaker = '#e4d7f5' MountainMeadow = '#149e60' Oasis = '#fef1d1' OceanGreen = '#44b984' OldGold = '#d5ae49' Perano = '#a4c2f4' PersianPink = '#f691b3' PigPink = '#fcdee8' Pueblo = '#822111' RedOrange = '#fb4c2f' RoyalBlue = '#3c78d8' RoyalPurple = '#653e9b' Salem = '#0b804b' Salomie = '#fcda83' SeaPink = '#efa093' Shamrock = '#3dc789' Silver = '#cccccc' Tabasco = '#ac2b16' Tequila = '#ffe6c7' Thunderbird = '#cc3a21' TropicalBlue = '#c9daf8' TulipTree = '#eaa041' Tundora = '#434343' VistaBlue = '#89d3b2' Watercourse = '#076239' WaterLeaf = '#a0eac9' White = '#ffffff' YellowOrange = '#ffad47' .PARAMETER TextColor The text color of the label Options and their corresponding hex code: Amethyst = '#8e63ce' BananaMania = '#fce8b3' Bermuda = '#68dfa9' BilobaFlower = '#b694e8' Black = '#000000' BlueRomance = '#c6f3de' BrandyPunch = '#cf8933' BurntSienna = '#e66550' Cadillac = '#b65775' Camelot = '#83334c' CeruleanBlue = '#285bac' ChathamsBlue = '#1c4587' Concrete = '#f3f3f3' CornflowerBlue = '#4a86e8' LightCornflowerBlue = '#6d9eeb' CreamCan = '#f2c960' Cupid = '#fbc8d9' DeepBlush = '#e07798' Desert = '#a46a21' DoveGray = '#666666' DustyGray = '#999999' Eucalyptus = '#2a9c68' Flesh = '#ffd6a2' FringyFlower = '#b9e4d0' Gallery = '#efefef' Goldenrod = '#fad165' Illusion = '#f7a7c0' Jewel = '#1a764d' Koromiko = '#ffbc6b' LightMountainMeadow = '#16a766' LightShamrock = '#43d692' LuxorGold = '#aa8831' MandysPink = '#f6c5be' MediumPurple = '#a479e2' Meteorite = '#41236d' MoonRaker = '#d0bcf1' LightMoonRaker = '#e4d7f5' MountainMeadow = '#149e60' Oasis = '#fef1d1' OceanGreen = '#44b984' OldGold = '#d5ae49' Perano = '#a4c2f4' PersianPink = '#f691b3' PigPink = '#fcdee8' Pueblo = '#822111' RedOrange = '#fb4c2f' RoyalBlue = '#3c78d8' RoyalPurple = '#653e9b' Salem = '#0b804b' Salomie = '#fcda83' SeaPink = '#efa093' Shamrock = '#3dc789' Silver = '#cccccc' Tabasco = '#ac2b16' Tequila = '#ffe6c7' Thunderbird = '#cc3a21' TropicalBlue = '#c9daf8' TulipTree = '#eaa041' Tundora = '#434343' VistaBlue = '#89d3b2' Watercourse = '#076239' WaterLeaf = '#a0eac9' White = '#ffffff' YellowOrange = '#ffad47' .PARAMETER User The primary email of the user to add the label to Defaults to the AdminEmail user .EXAMPLE New-GSGmailLabel -Name Label1 Adds the label "Label1" to the AdminEmail #> [cmdletbinding()] Param ( [parameter(Mandatory = $true, Position = 0)] [string] $Name, [parameter(Mandatory = $false)] [ValidateSet('labelHide','labelShow','labelShowIfUnread')] [string] $LabelListVisibility = "labelShow", [parameter(Mandatory = $false)] [ValidateSet('show','hide')] [string] $MessageListVisibility = "show", [parameter(Mandatory = $false)] [ValidateSet('Amethyst','BananaMania','Bermuda','BilobaFlower','Black','BlueRomance','BrandyPunch','BurntSienna','Cadillac','Camelot','CeruleanBlue','ChathamsBlue','Concrete','CornflowerBlue','CreamCan','Cupid','DeepBlush','Desert','DoveGray','DustyGray','Eucalyptus','Flesh','FringyFlower','Gallery','Goldenrod','Illusion','Jewel','Koromiko','LightCornflowerBlue','LightMoonRaker','LightMountainMeadow','LightShamrock','LuxorGold','MandysPink','MediumPurple','Meteorite','MoonRaker','MountainMeadow','Oasis','OceanGreen','OldGold','Perano','PersianPink','PigPink','Pueblo','RedOrange','RoyalBlue','RoyalPurple','Salem','Salomie','SeaPink','Shamrock','Silver','Tabasco','Tequila','Thunderbird','TropicalBlue','TulipTree','Tundora','VistaBlue','Watercourse','WaterLeaf','White','YellowOrange')] [string] $BackgroundColor, [parameter(Mandatory = $false)] [ValidateSet('Amethyst','BananaMania','Bermuda','BilobaFlower','Black','BlueRomance','BrandyPunch','BurntSienna','Cadillac','Camelot','CeruleanBlue','ChathamsBlue','Concrete','CornflowerBlue','CreamCan','Cupid','DeepBlush','Desert','DoveGray','DustyGray','Eucalyptus','Flesh','FringyFlower','Gallery','Goldenrod','Illusion','Jewel','Koromiko','LightCornflowerBlue','LightMoonRaker','LightMountainMeadow','LightShamrock','LuxorGold','MandysPink','MediumPurple','Meteorite','MoonRaker','MountainMeadow','Oasis','OceanGreen','OldGold','Perano','PersianPink','PigPink','Pueblo','RedOrange','RoyalBlue','RoyalPurple','Salem','Salomie','SeaPink','Shamrock','Silver','Tabasco','Tequila','Thunderbird','TropicalBlue','TulipTree','Tundora','VistaBlue','Watercourse','WaterLeaf','White','YellowOrange')] [string] $TextColor, [parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail", "UserKey", "Mail")] [ValidateNotNullOrEmpty()] [string] $User = $Script:PSGSuite.AdminEmail ) Begin { $colorDict = @{ Amethyst = '#8e63ce' BananaMania = '#fce8b3' Bermuda = '#68dfa9' BilobaFlower = '#b694e8' Black = '#000000' BlueRomance = '#c6f3de' BrandyPunch = '#cf8933' BurntSienna = '#e66550' Cadillac = '#b65775' Camelot = '#83334c' CeruleanBlue = '#285bac' ChathamsBlue = '#1c4587' Concrete = '#f3f3f3' CornflowerBlue = '#4a86e8' LightCornflowerBlue = '#6d9eeb' CreamCan = '#f2c960' Cupid = '#fbc8d9' DeepBlush = '#e07798' Desert = '#a46a21' DoveGray = '#666666' DustyGray = '#999999' Eucalyptus = '#2a9c68' Flesh = '#ffd6a2' FringyFlower = '#b9e4d0' Gallery = '#efefef' Goldenrod = '#fad165' Illusion = '#f7a7c0' Jewel = '#1a764d' Koromiko = '#ffbc6b' LightMountainMeadow = '#16a766' LightShamrock = '#43d692' LuxorGold = '#aa8831' MandysPink = '#f6c5be' MediumPurple = '#a479e2' Meteorite = '#41236d' MoonRaker = '#d0bcf1' LightMoonRaker = '#e4d7f5' MountainMeadow = '#149e60' Oasis = '#fef1d1' OceanGreen = '#44b984' OldGold = '#d5ae49' Perano = '#a4c2f4' PersianPink = '#f691b3' PigPink = '#fcdee8' Pueblo = '#822111' RedOrange = '#fb4c2f' RoyalBlue = '#3c78d8' RoyalPurple = '#653e9b' Salem = '#0b804b' Salomie = '#fcda83' SeaPink = '#efa093' Shamrock = '#3dc789' Silver = '#cccccc' Tabasco = '#ac2b16' Tequila = '#ffe6c7' Thunderbird = '#cc3a21' TropicalBlue = '#c9daf8' TulipTree = '#eaa041' Tundora = '#434343' VistaBlue = '#89d3b2' Watercourse = '#076239' WaterLeaf = '#a0eac9' White = '#ffffff' YellowOrange = '#ffad47' } if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://mail.google.com' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $User } $service = New-GoogleService @serviceParams } Process { $body = New-Object 'Google.Apis.Gmail.v1.Data.Label' foreach ($prop in $PSBoundParameters.Keys | Where-Object {$body.PSObject.Properties.Name -contains $_}) { $body.$prop = $PSBoundParameters[$prop] } if ($PSBoundParameters.Keys -contains 'BackgroundColor' -or $PSBoundParameters.Keys -contains 'TextColor') { $color = New-Object 'Google.Apis.Gmail.v1.Data.LabelColor' foreach ($prop in $PSBoundParameters.Keys | Where-Object {$color.PSObject.Properties.Name -contains $_}) { $color.$prop = $colorDict[$PSBoundParameters[$prop]] } $body.Color = $color } try { Write-Verbose "Creating Label '$Name' for user '$User'" $request = $service.Users.Labels.Create($body, $User) $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'New-GSGmailLabel' function New-GSGmailSMIMEInfo { <# .SYNOPSIS Adds Gmail S/MIME info .DESCRIPTION Adds Gmail S/MIME info .PARAMETER SendAsEmail The email address that appears in the "From:" header for mail sent using this alias. .PARAMETER Pkcs12 PKCS#12 format containing a single private/public key pair and certificate chain. This format is only accepted from client for creating a new SmimeInfo and is never returned, because the private key is not intended to be exported. PKCS#12 may be encrypted, in which case encryptedKeyPassword should be set appropriately. .PARAMETER EncryptedKeyPassword Encrypted key password, when key is encrypted. .PARAMETER IsDefault Whether this SmimeInfo is the default one for this user's send-as address. .PARAMETER User The user's email address .EXAMPLE New-GSGmailSMIMEInfo -SendAsEmail 'joe@otherdomain.com' -Pkcs12 .\MyCert.pfx -User joe@domain.com Creates a specified S/MIME for Joe's SendAsEmail 'joe@otherdomain.com' using the provided PKCS12 certificate #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)] [string] $SendAsEmail, [parameter(Mandatory = $true)] [ValidateScript({ if (!(Test-Path $_)) { throw "Please enter a valid file path." } elseif ($_ -notlike "*.pfx" -and $_ -notlike "*.p12") { throw "Pkcs12 must be a .pfx or .p12 file" } else { $true } })] [string] $Pkcs12, [parameter(Mandatory = $false)] [SecureString] $EncryptedKeyPassword, [parameter(Mandatory = $false)] [Switch] $IsDefault, [parameter(Mandatory = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [string] $User ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/gmail.settings.basic' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $User } $service = New-GoogleService @serviceParams } Process { try { $body = New-Object 'Google.Apis.Gmail.v1.Data.SmimeInfo' foreach ($key in $PSBoundParameters.Keys | Where-Object {$body.PSObject.Properties.Name -contains $_}) { switch ($key) { EncryptedKeyPassword { $body.$key = (New-Object PSCredential "user",$PSBoundParameters[$key]).GetNetworkCredential().Password } Pkcs12 { $p12String = Convert-Base64 -From NormalString -To WebSafeBase64String -String "$([System.IO.File]::ReadAllText((Resolve-Path $PSBoundParameters[$key]).Path))" $body.$key = $p12String } Default { $body.$prop = $PSBoundParameters[$prop] } } } Write-Verbose "Adding new S/MIME of SendAsEmail '$SendAsEmail' for user '$User' using Certificate '$Pkcs12'" $request = $service.Users.Settings.SendAs.SmimeInfo.Insert($body,$User,$SendAsEmail) $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'New-GSGmailSMIMEInfo' function Remove-GSGmailFilter { <# .SYNOPSIS Removes a Gmail filter .DESCRIPTION Removes a Gmail filter .PARAMETER User The primary email of the user to remove the filter from Defaults to the AdminEmail user .PARAMETER FilterId The unique Id of the filter to remove .PARAMETER Raw If $true, returns the raw response. If not passed or -Raw:$false, response is formatted as a flat object for readability .EXAMPLE Remove-GSGmailFilter -FilterId ANe1Bmj5l3089jd3k1eQbY90g9rXswjS03LVOw Removes the Filter from the AdminEmail user after confirmation #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [string] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)] [Alias("Id")] [string[]] $FilterId, [parameter(Mandatory = $false)] [switch] $Raw ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/gmail.settings.basic' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $User } $service = New-GoogleService @serviceParams } Process { try { foreach ($fil in $FilterId) { if ($PSCmdlet.ShouldProcess("Deleting Filter Id '$fil' from user '$User'")) { Write-Verbose "Deleting Filter Id '$fil' from user '$User'" $request = $service.Users.Settings.Filters.Delete($User,$fil) $request.Execute() Write-Verbose "Filter Id '$fil' deleted successfully from user '$User'" } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Remove-GSGmailFilter' function Remove-GSGmailLabel { <# .SYNOPSIS Removes a Gmail label .DESCRIPTION Removes a Gmail label .PARAMETER LabelId The unique Id of the label to remove .PARAMETER User The primary email of the user to remove the label from Defaults to the AdminEmail user .EXAMPLE Remove-GSGmailLabel -LabelId ANe1Bmj5l3089jd3k1eQbY90g9rXswjS03LVOw Removes the Label from the AdminEmail user after confirmation #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $true, Position = 0, ValueFromPipelineByPropertyName = $true)] [Alias("Id")] [string[]] $LabelId, [parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail", "UserKey", "Mail")] [ValidateNotNullOrEmpty()] [string] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://mail.google.com' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $User } $service = New-GoogleService @serviceParams } Process { foreach ($label in $LabelId) { try { if ($PSCmdlet.ShouldProcess("Deleting Label Id '$label' from user '$User'")) { Write-Verbose "Deleting Label Id '$label' from user '$User'" $request = $service.Users.Labels.Delete($User, $label) $request.Execute() Write-Verbose "Label Id '$label' deleted successfully from user '$User'" } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Remove-GSGmailLabel' function Remove-GSGmailMessage { <# .SYNOPSIS Removes a Gmail message from the user .DESCRIPTION Removes a Gmail message from the user .PARAMETER Id The Id of the message to remove .PARAMETER Filter The Gmail query to pull the list of messages to remove instead of passing the MessageId directly .PARAMETER MaxToModify The maximum amount of emails you would like to remove. Use this with the `Filter` parameter as a safeguard. .PARAMETER Method The method used to delete the message Available values are: * "Trash": moves the message to the TRASH label (Default - preferred method, as this is recoverable) * "Delete": permanently deletes the message (NON-RECOVERABLE!) Default value is 'Trash' .PARAMETER User The primary email of the user to remove the message from Defaults to the AdminEmail user .EXAMPLE Remove-GSGmailMessage -User joe -Id 161622d7b76b7e1e,1616227c34d435f2 Moves the 2 message Id's from Joe's inbox into their TRASH after confirmation #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High",DefaultParameterSetName = "MessageId")] Param ( [parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true,ParameterSetName = "MessageId")] [Alias('MessageId')] [String[]] $Id, [parameter(Mandatory = $true, ParameterSetName = "Filter")] [Alias('Query')] [string] $Filter, [parameter(Mandatory = $false,ParameterSetName = "Filter")] [int] $MaxToModify, [parameter(Mandatory = $false)] [ValidateSet('Trash','Delete')] [String] $Method = 'Trash', [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [string] $User = $Script:PSGSuite.AdminEmail ) Process { if ($MyInvocation.InvocationName -eq 'Move-GSGmailMessageToTrash') { $Method = 'Trash' } if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://mail.google.com' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $User } $msgId = switch ($PSCmdlet.ParameterSetName) { MessageId { $Id } Filter { (Get-GSGmailMessageList -Filter $Filter -User $User).Id } } if ($PSBoundParameters.Keys -contains 'MaxToModify' -and $msgId.Count -gt $MaxToModify) { Write-Error "MaxToModify is set to $MaxToModify but total modifications are $($msgId.Count). No action taken." } else { $service = New-GoogleService @serviceParams try { foreach ($mId in $msgId) { $request = switch ($Method) { Trash { $service.Users.Messages.Trash($User,$mId) $message = "moved to TRASH" } Delete { $service.Users.Messages.Delete($User,$mId) $message = "deleted" } } if ($PSCmdlet.ShouldProcess("Removing Message Id '$mId' for user '$User'")) { Write-Verbose "Removing Message Id '$mId' for user '$User'" $res = $request.Execute() if ($res) { $res | Add-Member -MemberType NoteProperty -Name 'User' -Value $U -PassThru } Write-Verbose "Message ID '$mId' successfully $message for user '$User'" } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Remove-GSGmailMessage' function Remove-GSGmailSMIMEInfo { <# .SYNOPSIS Removes Gmail S/MIME info .DESCRIPTION Removes Gmail S/MIME info .PARAMETER SendAsEmail The email address that appears in the "From:" header for mail sent using this alias. .PARAMETER Id The immutable ID for the SmimeInfo .PARAMETER User The user's email address Defaults to the AdminEmail user .EXAMPLE Remove-GSGmailSMIMEInfo -SendAsEmail 'joe@otherdomain.com' -Id 1008396210820120578939 -User joe@domain.com Removes the specified S/MIME info for Joe's SendAsEmail 'joe@otherdomain.com'. #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)] [string] $SendAsEmail, [parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)] [string[]] $Id, [parameter(Mandatory = $false)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [string] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/gmail.settings.basic' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $User } $service = New-GoogleService @serviceParams } Process { foreach ($I in $Id) { try { if ($PSCmdlet.ShouldProcess("Removing S/MIME Id '$I' of SendAsEmail '$SendAsEmail' for user '$User'")) { $request = $service.Users.Settings.SendAs.SmimeInfo.Delete($User,$SendAsEmail,$I) $request.Execute() Write-Verbose "Successfully removed S/MIME Id '$I' of SendAsEmail '$SendAsEmail' for user '$User'" } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Remove-GSGmailSMIMEInfo' function Restore-GSGmailMessage { <# .SYNOPSIS Restores a trashed message to the inbox .DESCRIPTION Restores a trashed message to the inbox .PARAMETER User The primary email of the user to restore the message for Defaults to the AdminEmail user .PARAMETER Id The Id of the message to restore .EXAMPLE Restore-GSGmailMessage -User joe -Id 161622d7b76b7e1e,1616227c34d435f2 Restores the 2 message Id's from Joe's TRASH back to their inbox #> [cmdletbinding()] Param ( [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [string] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)] [Alias('MessageID')] [String[]] $Id ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://mail.google.com' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $User } $service = New-GoogleService @serviceParams } Process { try { foreach ($mId in $Id) { $request = $service.Users.Messages.Untrash($User,$mId) Write-Verbose "Removing Message Id '$mId' from TRASH for user '$User'" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $U -PassThru } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Restore-GSGmailMessage' function Send-GmailMessage { <# .SYNOPSIS Sends a Gmail message .DESCRIPTION Sends a Gmail message. Designed for parity with Send-GmailMessage .PARAMETER From The primary email of the user that is sending the message. This MUST be a user account owned by the customer, as the Gmail Service must be built under this user's context and will fail if a group or alias is passed instead Defaults to the AdminEmail user .PARAMETER Subject The subject of the email .PARAMETER Body The email body. Supports HTML when used in conjunction with the -BodyAsHtml parameter .PARAMETER To The To recipient(s) of the email .PARAMETER CC The Cc recipient(s) of the email .PARAMETER BCC The Bcc recipient(s) of the email .PARAMETER Attachments The attachment(s) of the email .PARAMETER BodyAsHtml If passed, renders the HTML content of the body on send .EXAMPLE Send-GmailMessage -From Joe -To john.doe@domain.com -Subject "New Pricing Models" -Body $body -BodyAsHtml -Attachments 'C:\Reports\PricingModel_2018.xlsx' Sends a message from Joe to john.doe@domain.com with HTML body and an Excel spreadsheet attached #> [cmdletbinding()] Param ( [parameter(Mandatory = $false)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String] $From = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $true)] [string] $Subject, [parameter(Mandatory = $false)] [string] $Body, [parameter(Mandatory = $false)] [string[]] $To, [parameter(Mandatory = $false)] [string[]] $CC, [parameter(Mandatory = $false)] [string[]] $BCC, [parameter(Mandatory = $false)] [ValidateScript( {Test-Path $_})] [string[]] $Attachments, [parameter(Mandatory = $false)] [switch] $BodyAsHtml ) Process { $User = $From -replace ".*<","" -replace ">","" if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail $From = $User } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" $From = $User } $serviceParams = @{ Scope = 'https://mail.google.com' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $User } $service = New-GoogleService @serviceParams $messageParams = @{ From = $From Subject = $Subject ReturnConstructedMessage = $true } if ($To) { $messageParams.Add("To",@($To)) } if ($Body) { $messageParams.Add("Body",$Body) } if ($CC) { $messageParams.Add("CC",@($CC)) } if ($BCC) { $messageParams.Add("BCC",@($BCC)) } if ($Attachments) { $messageParams.Add("Attachment",@($Attachments)) } if ($BodyAsHtml) { $messageParams.Add("BodyAsHtml",$true) } $raw = New-MimeMessage @messageParams | Convert-Base64 -From NormalString -To WebSafeBase64String try { $bodySend = New-Object 'Google.Apis.Gmail.v1.Data.Message' -Property @{ Raw = $raw } $request = $service.Users.Messages.Send($bodySend,$User) Write-Verbose "Sending Message '$Subject' from user '$User'" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Send-GmailMessage' function Update-GSGmailAutoForwardingSettings { <# .SYNOPSIS Updates the auto-forwarding setting for the specified account. A verified forwarding address must be specified when auto-forwarding is enabled. .DESCRIPTION Updates the auto-forwarding setting for the specified account. A verified forwarding address must be specified when auto-forwarding is enabled. .PARAMETER User The user to update the AutoForwarding settings for .PARAMETER Disposition The state that a message should be left in after it has been forwarded. Acceptable values are: * "archive": Archive the message. * "dispositionUnspecified": Unspecified disposition. * "leaveInInbox": Leave the message in the INBOX. * "markRead": Leave the message in the INBOX and mark it as read. * "trash": Move the message to the TRASH. .PARAMETER EmailAddress Email address to which all incoming messages are forwarded. This email address must be a verified member of the forwarding addresses. .PARAMETER Enabled Whether all incoming mail is automatically forwarded to another address. .EXAMPLE Update-GSGmailAutoForwardingSettings -User me -Disposition leaveInInbox -EmailAddress joe@domain.com -Enabled Enables auto forwarding of all mail for the AdminEmail user. Forwarded mail will be left in their inbox. #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [string] $User, [parameter(Mandatory = $false)] [ValidateSet('archive','dispositionUnspecified','leaveInInbox','markRead','trash')] [string] $Disposition, [parameter(Mandatory = $false)] [string] $EmailAddress, [parameter(Mandatory = $false)] [switch] $Enabled ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/gmail.settings.sharing' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $User } $service = New-GoogleService @serviceParams } Process { try { $body = New-Object 'Google.Apis.Gmail.v1.Data.AutoForwarding' foreach ($prop in $PSBoundParameters.Keys | Where-Object {$body.PSObject.Properties.Name -contains $_}) { $body.$prop = $PSBoundParameters[$prop] } $request = $service.Users.Settings.UpdateAutoForwarding($body,$User) Write-Verbose "Updating AutoForwarding settings for user '$User'" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Update-GSGmailAutoForwardingSettings' function Update-GSGmailImapSettings { <# .SYNOPSIS Updates IMAP settings .DESCRIPTION Updates IMAP settings .PARAMETER User The user to update the IMAP settings for .PARAMETER AutoExpunge If this value is true, Gmail will immediately expunge a message when it is marked as deleted in IMAP. Otherwise, Gmail will wait for an update from the client before expunging messages marked as deleted. .PARAMETER Enabled Whether IMAP is enabled for the account. .PARAMETER ExpungeBehavior The action that will be executed on a message when it is marked as deleted and expunged from the last visible IMAP folder. Acceptable values are: * "archive": Archive messages marked as deleted. * "deleteForever": Immediately and permanently delete messages marked as deleted. The expunged messages cannot be recovered. * "expungeBehaviorUnspecified": Unspecified behavior. * "trash": Move messages marked as deleted to the trash. .PARAMETER MaxFolderSize An optional limit on the number of messages that an IMAP folder may contain. Legal values are 0, 1000, 2000, 5000 or 10000. A value of zero is interpreted to mean that there is no limit. .EXAMPLE Update-GSGmailImapSettings -Enabled:$false -User me Disables IMAP for the AdminEmail user #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [string] $User, [parameter(Mandatory = $false)] [switch] $AutoExpunge, [parameter(Mandatory = $false)] [switch] $Enabled, [parameter(Mandatory = $false)] [ValidateSet('archive','deleteForever','expungeBehaviorUnspecified','trash')] [string] $ExpungeBehavior, [parameter(Mandatory = $false)] [ValidateSet(0,1000,2000,5000,10000)] [int] $MaxFolderSize ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/gmail.settings.basic' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $User } $service = New-GoogleService @serviceParams } Process { try { $body = New-Object 'Google.Apis.Gmail.v1.Data.ImapSettings' foreach ($prop in $PSBoundParameters.Keys | Where-Object {$body.PSObject.Properties.Name -contains $_}) { $body.$prop = $PSBoundParameters[$prop] } $request = $service.Users.Settings.UpdateImap($body,$User) Write-Verbose "Updating IMAP settings for user '$User'" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Update-GSGmailImapSettings' function Update-GSGmailLabel { <# .SYNOPSIS Updates Gmail label information for the specified labelid .DESCRIPTION Updates Gmail label information for the specified labelid .PARAMETER LabelId The unique Id of the label to update .PARAMETER LabelListVisibility The visibility of the label in the label list in the Gmail web interface. Acceptable values are: * "labelHide": Do not show the label in the label list. * "labelShow": Show the label in the label list. (Default) * "labelShowIfUnread": Show the label if there are any unread messages with that label. .PARAMETER MessageListVisibility The visibility of messages with this label in the message list in the Gmail web interface. Acceptable values are: * "hide": Do not show the label in the message list. * "show": Show the label in the message list. (Default) .PARAMETER Name The display name of the label .PARAMETER BackgroundColor The background color of the label Options and their corresponding hex code: Amethyst = '#8e63ce' BananaMania = '#fce8b3' Bermuda = '#68dfa9' BilobaFlower = '#b694e8' Black = '#000000' BlueRomance = '#c6f3de' BrandyPunch = '#cf8933' BurntSienna = '#e66550' Cadillac = '#b65775' Camelot = '#83334c' CeruleanBlue = '#285bac' ChathamsBlue = '#1c4587' Concrete = '#f3f3f3' CornflowerBlue = '#4a86e8' LightCornflowerBlue = '#6d9eeb' CreamCan = '#f2c960' Cupid = '#fbc8d9' DeepBlush = '#e07798' Desert = '#a46a21' DoveGray = '#666666' DustyGray = '#999999' Eucalyptus = '#2a9c68' Flesh = '#ffd6a2' FringyFlower = '#b9e4d0' Gallery = '#efefef' Goldenrod = '#fad165' Illusion = '#f7a7c0' Jewel = '#1a764d' Koromiko = '#ffbc6b' LightMountainMeadow = '#16a766' LightShamrock = '#43d692' LuxorGold = '#aa8831' MandysPink = '#f6c5be' MediumPurple = '#a479e2' Meteorite = '#41236d' MoonRaker = '#d0bcf1' LightMoonRaker = '#e4d7f5' MountainMeadow = '#149e60' Oasis = '#fef1d1' OceanGreen = '#44b984' OldGold = '#d5ae49' Perano = '#a4c2f4' PersianPink = '#f691b3' PigPink = '#fcdee8' Pueblo = '#822111' RedOrange = '#fb4c2f' RoyalBlue = '#3c78d8' RoyalPurple = '#653e9b' Salem = '#0b804b' Salomie = '#fcda83' SeaPink = '#efa093' Shamrock = '#3dc789' Silver = '#cccccc' Tabasco = '#ac2b16' Tequila = '#ffe6c7' Thunderbird = '#cc3a21' TropicalBlue = '#c9daf8' TulipTree = '#eaa041' Tundora = '#434343' VistaBlue = '#89d3b2' Watercourse = '#076239' WaterLeaf = '#a0eac9' White = '#ffffff' YellowOrange = '#ffad47' .PARAMETER TextColor The text color of the label Options and their corresponding hex code: Amethyst = '#8e63ce' BananaMania = '#fce8b3' Bermuda = '#68dfa9' BilobaFlower = '#b694e8' Black = '#000000' BlueRomance = '#c6f3de' BrandyPunch = '#cf8933' BurntSienna = '#e66550' Cadillac = '#b65775' Camelot = '#83334c' CeruleanBlue = '#285bac' ChathamsBlue = '#1c4587' Concrete = '#f3f3f3' CornflowerBlue = '#4a86e8' LightCornflowerBlue = '#6d9eeb' CreamCan = '#f2c960' Cupid = '#fbc8d9' DeepBlush = '#e07798' Desert = '#a46a21' DoveGray = '#666666' DustyGray = '#999999' Eucalyptus = '#2a9c68' Flesh = '#ffd6a2' FringyFlower = '#b9e4d0' Gallery = '#efefef' Goldenrod = '#fad165' Illusion = '#f7a7c0' Jewel = '#1a764d' Koromiko = '#ffbc6b' LightMountainMeadow = '#16a766' LightShamrock = '#43d692' LuxorGold = '#aa8831' MandysPink = '#f6c5be' MediumPurple = '#a479e2' Meteorite = '#41236d' MoonRaker = '#d0bcf1' LightMoonRaker = '#e4d7f5' MountainMeadow = '#149e60' Oasis = '#fef1d1' OceanGreen = '#44b984' OldGold = '#d5ae49' Perano = '#a4c2f4' PersianPink = '#f691b3' PigPink = '#fcdee8' Pueblo = '#822111' RedOrange = '#fb4c2f' RoyalBlue = '#3c78d8' RoyalPurple = '#653e9b' Salem = '#0b804b' Salomie = '#fcda83' SeaPink = '#efa093' Shamrock = '#3dc789' Silver = '#cccccc' Tabasco = '#ac2b16' Tequila = '#ffe6c7' Thunderbird = '#cc3a21' TropicalBlue = '#c9daf8' TulipTree = '#eaa041' Tundora = '#434343' VistaBlue = '#89d3b2' Watercourse = '#076239' WaterLeaf = '#a0eac9' White = '#ffffff' YellowOrange = '#ffad47' .PARAMETER User The user to update label information for Defaults to the AdminEmail user .EXAMPLE Update-GSGmailLabel -User user@domain.com -LabelId Label_79 -BackgroundColor Black -TextColor Bermuda Updates the specified Gmail label with new background and text colors .EXAMPLE Get-GSGmailLabel | Where-Object {$_.LabelListVisibility -eq 'labelShowIfUnread'} | Update-GSGmailLabel -LabelListVisibility labelShow -BackgroundColor Bermuda -TextColor Tundora Updates all labels with LabelListVisibility of 'labelShowIfUnread' with new background and text colors and sets all of them to always show #> [cmdletbinding()] Param ( [parameter(Mandatory = $true, Position = 0, ValueFromPipelineByPropertyName = $true)] [Alias("Id")] [string[]] $LabelId, [parameter(Mandatory = $false, Position = 1)] [ValidateSet("labelHide","labelShow","labelShowIfUnread")] [string] $LabelListVisibility, [parameter(Mandatory = $false, Position = 2)] [ValidateSet("hide","show")] [string] $MessageListVisibility, [parameter(Mandatory = $false, Position = 3)] [string] $Name, [parameter(Mandatory = $false)] [ValidateSet('Amethyst','BananaMania','Bermuda','BilobaFlower','Black','BlueRomance','BrandyPunch','BurntSienna','Cadillac','Camelot','CeruleanBlue','ChathamsBlue','Concrete','CornflowerBlue','CreamCan','Cupid','DeepBlush','Desert','DoveGray','DustyGray','Eucalyptus','Flesh','FringyFlower','Gallery','Goldenrod','Illusion','Jewel','Koromiko','LightCornflowerBlue','LightMoonRaker','LightMountainMeadow','LightShamrock','LuxorGold','MandysPink','MediumPurple','Meteorite','MoonRaker','MountainMeadow','Oasis','OceanGreen','OldGold','Perano','PersianPink','PigPink','Pueblo','RedOrange','RoyalBlue','RoyalPurple','Salem','Salomie','SeaPink','Shamrock','Silver','Tabasco','Tequila','Thunderbird','TropicalBlue','TulipTree','Tundora','VistaBlue','Watercourse','WaterLeaf','White','YellowOrange')] [string] $BackgroundColor, [parameter(Mandatory = $false)] [ValidateSet('Amethyst','BananaMania','Bermuda','BilobaFlower','Black','BlueRomance','BrandyPunch','BurntSienna','Cadillac','Camelot','CeruleanBlue','ChathamsBlue','Concrete','CornflowerBlue','CreamCan','Cupid','DeepBlush','Desert','DoveGray','DustyGray','Eucalyptus','Flesh','FringyFlower','Gallery','Goldenrod','Illusion','Jewel','Koromiko','LightCornflowerBlue','LightMoonRaker','LightMountainMeadow','LightShamrock','LuxorGold','MandysPink','MediumPurple','Meteorite','MoonRaker','MountainMeadow','Oasis','OceanGreen','OldGold','Perano','PersianPink','PigPink','Pueblo','RedOrange','RoyalBlue','RoyalPurple','Salem','Salomie','SeaPink','Shamrock','Silver','Tabasco','Tequila','Thunderbird','TropicalBlue','TulipTree','Tundora','VistaBlue','Watercourse','WaterLeaf','White','YellowOrange')] [string] $TextColor, [parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail", "UserKey", "Mail")] [ValidateNotNullOrEmpty()] [string] $User = $Script:PSGSuite.AdminEmail ) Begin { $colorDict = @{ Amethyst = '#8e63ce' BananaMania = '#fce8b3' Bermuda = '#68dfa9' BilobaFlower = '#b694e8' Black = '#000000' BlueRomance = '#c6f3de' BrandyPunch = '#cf8933' BurntSienna = '#e66550' Cadillac = '#b65775' Camelot = '#83334c' CeruleanBlue = '#285bac' ChathamsBlue = '#1c4587' Concrete = '#f3f3f3' CornflowerBlue = '#4a86e8' LightCornflowerBlue = '#6d9eeb' CreamCan = '#f2c960' Cupid = '#fbc8d9' DeepBlush = '#e07798' Desert = '#a46a21' DoveGray = '#666666' DustyGray = '#999999' Eucalyptus = '#2a9c68' Flesh = '#ffd6a2' FringyFlower = '#b9e4d0' Gallery = '#efefef' Goldenrod = '#fad165' Illusion = '#f7a7c0' Jewel = '#1a764d' Koromiko = '#ffbc6b' LightMountainMeadow = '#16a766' LightShamrock = '#43d692' LuxorGold = '#aa8831' MandysPink = '#f6c5be' MediumPurple = '#a479e2' Meteorite = '#41236d' MoonRaker = '#d0bcf1' LightMoonRaker = '#e4d7f5' MountainMeadow = '#149e60' Oasis = '#fef1d1' OceanGreen = '#44b984' OldGold = '#d5ae49' Perano = '#a4c2f4' PersianPink = '#f691b3' PigPink = '#fcdee8' Pueblo = '#822111' RedOrange = '#fb4c2f' RoyalBlue = '#3c78d8' RoyalPurple = '#653e9b' Salem = '#0b804b' Salomie = '#fcda83' SeaPink = '#efa093' Shamrock = '#3dc789' Silver = '#cccccc' Tabasco = '#ac2b16' Tequila = '#ffe6c7' Thunderbird = '#cc3a21' TropicalBlue = '#c9daf8' TulipTree = '#eaa041' Tundora = '#434343' VistaBlue = '#89d3b2' Watercourse = '#076239' WaterLeaf = '#a0eac9' White = '#ffffff' YellowOrange = '#ffad47' } } Process { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://mail.google.com' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $User } $service = New-GoogleService @serviceParams $body = New-Object 'Google.Apis.Gmail.v1.Data.Label' foreach ($prop in $PSBoundParameters.Keys | Where-Object {$body.PSObject.Properties.Name -contains $_}) { $body.$prop = $PSBoundParameters[$prop] } if ($PSBoundParameters.Keys -contains 'BackgroundColor' -or $PSBoundParameters.Keys -contains 'TextColor') { $color = New-Object 'Google.Apis.Gmail.v1.Data.LabelColor' foreach ($prop in $PSBoundParameters.Keys | Where-Object {$color.PSObject.Properties.Name -contains $_}) { $color.$prop = $colorDict[$PSBoundParameters[$prop]] } $body.Color = $color } foreach ($label in $LabelId) { try { Write-Verbose "Updating Label Id '$label' for user '$User'" $request = $service.Users.Labels.Patch($body, $User, $label) $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Update-GSGmailLabel' function Update-GSGmailMessageLabels { <# .SYNOPSIS Updates Gmail label information for the specified message .DESCRIPTION Updates Gmail label information for the specified message .PARAMETER MessageId The unique Id of the message to update. .PARAMETER Filter The Gmail query to pull the list of messages to update instead of passing the MessageId directly. .PARAMETER MaxToModify The maximum amount of emails you would like to remove. Use this with the `Filter` parameter as a safeguard. .PARAMETER AddLabel The label(s) to add to the message. This supports either the unique LabelId or the Display Name for the label .PARAMETER RemoveLabel The label(s) to remove from the message. This supports either the unique LabelId or the Display Name for the label .PARAMETER User The user to update message labels for Defaults to the AdminEmail user .EXAMPLE Set-GSGmailLabel -user user@domain.com -LabelId Label_798170282134616520 - Gets the Gmail labels of the AdminEmail user #> [cmdletbinding(DefaultParameterSetName = "MessageId")] Param ( [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = "MessageId")] [Alias("Id")] [string[]] $MessageId, [parameter(Mandatory = $true, ParameterSetName = "Filter")] [Alias('Query')] [string] $Filter, [parameter(Mandatory = $false,ParameterSetName = "Filter")] [int] $MaxToModify, [parameter(Mandatory = $false)] [string[]] $AddLabel, [parameter(Mandatory = $false)] [string[]] $RemoveLabel, [parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail", "UserKey", "Mail")] [ValidateNotNullOrEmpty()] [string] $User = $Script:PSGSuite.AdminEmail ) Process { if ($PSBoundParameters.Keys -notcontains 'AddLabel' -and $PSBoundParameters.Keys -notcontains 'RemoveLabel') { throw "You must specify a value for either AddLabel or RemoveLabel!" } if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://mail.google.com' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $User } $msgId = switch ($PSCmdlet.ParameterSetName) { MessageId { $MessageId } Filter { (Get-GSGmailMessageList -Filter $Filter -User $User).Id } } if ($PSBoundParameters.Keys -contains 'MaxToModify' -and $msgId.Count -gt $MaxToModify) { Write-Error "MaxToModify is set to $MaxToModify but total modifications are $($msgId.Count). No action taken." } else { $service = New-GoogleService @serviceParams $userLabels = @{} Get-GSGmailLabel -User $User -Verbose:$false | ForEach-Object { $userLabels[$_.Name] = $_.Id } $body = New-Object 'Google.Apis.Gmail.v1.Data.ModifyMessageRequest' if ($PSBoundParameters.Keys -contains 'AddLabel') { $addLs = New-Object 'System.Collections.Generic.List[System.String]' foreach ($label in $AddLabel) { try { $addLs.Add($userLabels[$label]) } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } $body.AddLabelIds = $addLs } if ($PSBoundParameters.Keys -contains 'RemoveLabel') { $remLs = New-Object 'System.Collections.Generic.List[System.String]' foreach ($label in $RemoveLabel) { try { $remLs.Add($userLabels[$label]) } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } $body.RemoveLabelIds = $remLs } foreach ($message in $msgId) { try { $request = $service.Users.Messages.Modify($body, $User, $message) Write-Verbose "Updating Labels on Message '$message' for user '$User'" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } } Export-ModuleMember -Function 'Update-GSGmailMessageLabels' function Update-GSGmailPopSettings { <# .SYNOPSIS Updates POP settings .DESCRIPTION Updates POP settings .PARAMETER User The user to update the POP settings for .PARAMETER AccessWindow The range of messages which are accessible via POP. Acceptable values are: * "accessWindowUnspecified": Unspecified range. * "allMail": Indicates that all unfetched messages are accessible via POP. * "disabled": Indicates that no messages are accessible via POP. * "fromNowOn": Indicates that unfetched messages received after some past point in time are accessible via POP. .PARAMETER Disposition The action that will be executed on a message after it has been fetched via POP. Acceptable values are: * "archive": Archive the message. * "dispositionUnspecified": Unspecified disposition. * "leaveInInbox": Leave the message in the INBOX. * "markRead": Leave the message in the INBOX and mark it as read. * "trash": Move the message to the TRASH. .EXAMPLE Update-GSGmailPopSettings -User me -AccessWindow allMail Sets the POP AccessWindow to 'allMail' for the AdminEmail user #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [string] $User, [parameter(Mandatory = $false)] [ValidateSet('accessWindowUnspecified','allMail','disabled','fromNowOn')] [string] $AccessWindow, [parameter(Mandatory = $false)] [ValidateSet('archive','dispositionUnspecified','leaveInInbox','markRead','trash')] [string] $Disposition ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/gmail.settings.basic' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $User } $service = New-GoogleService @serviceParams } Process { try { $body = New-Object 'Google.Apis.Gmail.v1.Data.PopSettings' foreach ($prop in $PSBoundParameters.Keys | Where-Object {$body.PSObject.Properties.Name -contains $_}) { $body.$prop = $PSBoundParameters[$prop] } $request = $service.Users.Settings.UpdatePop($body,$User) Write-Verbose "Updating POP settings for user '$User'" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Update-GSGmailPopSettings' function Update-GSGmailVacationSettings { <# .SYNOPSIS Updates vacation responder settings for the specified account. .DESCRIPTION Updates vacation responder settings for the specified account. .PARAMETER User The user to update the VacationSettings settings for .PARAMETER EnableAutoReply Flag that controls whether Gmail automatically replies to messages. .PARAMETER EndTime An optional end time for sending auto-replies. When this is specified, Gmail will automatically reply only to messages that it receives before the end time. If both startTime and endTime are specified, startTime must precede endTime. .PARAMETER ResponseBodyHtml Response body in HTML format. Gmail will sanitize the HTML before storing it. .PARAMETER ResponseBodyPlainText Response body in plain text format. .PARAMETER ResponseSubject Optional text to prepend to the subject line in vacation responses. In order to enable auto-replies, either the response subject or the response body must be nonempty. .PARAMETER RestrictToContacts Flag that determines whether responses are sent to recipients who are not in the user's list of contacts. .PARAMETER RestrictToDomain Flag that determines whether responses are sent to recipients who are outside of the user's domain. This feature is only available for G Suite users. .PARAMETER StartTime An optional start time for sending auto-replies. When this is specified, Gmail will automatically reply only to messages that it receives after the start time. If both startTime and endTime are specified, startTime must precede endTime. .EXAMPLE Update-GSGmailVacationSettings -User me -ResponseBodyHtml "I'm on vacation and will reply when I'm back in the office. Thanks!" -RestrictToDomain -EndTime (Get-Date).AddDays(7) -StartTime (Get-Date) -EnableAutoReply Enables the vacation auto-reply for the AdminEmail user. Auto-replies will be sent to other users in the same domain only. The vacation response is enabled for 7 days from the time that the command is sent. .EXAMPLE Update-GSGmailVacationSettings -User me -EnableAutoReply:$false Disables the vacaction auto-response for the AdminEmail user immediately. #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [string] $User, [parameter(Mandatory = $false)] [switch] $EnableAutoReply, [parameter(Mandatory = $false)] [datetime] $EndTime, [parameter(Mandatory = $false)] [string] $ResponseBodyHtml, [parameter(Mandatory = $false)] [string] $ResponseBodyPlainText, [parameter(Mandatory = $false)] [string] $ResponseSubject, [parameter(Mandatory = $false)] [switch] $RestrictToContacts, [parameter(Mandatory = $false)] [switch] $RestrictToDomain, [parameter(Mandatory = $false)] [datetime] $StartTime ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/gmail.settings.basic' ServiceType = 'Google.Apis.Gmail.v1.GmailService' User = $User } $service = New-GoogleService @serviceParams } Process { try { $body = New-Object 'Google.Apis.Gmail.v1.Data.VacationSettings' foreach ($prop in $PSBoundParameters.Keys | Where-Object {$body.PSObject.Properties.Name -contains $_}) { switch ($prop) { StartTime { $epochMs = (Convert-DateToEpoch $StartTime) $body.$prop = [long]$epochMs } EndTime { $epochMs = (Convert-DateToEpoch $EndTime) $body.$prop = [long]$epochMs } Default { $body.$prop = $PSBoundParameters[$prop] } } } $request = $service.Users.Settings.UpdateVacation($body,$User) Write-Verbose "Updating Vacation settings for user '$User'" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Update-GSGmailVacationSettings' function Add-GSGroupMember { <# .SYNOPSIS Adds a list of emails to a target group .DESCRIPTION Adds a list of emails to a target group. Designed for parity with Add-ADGroupMember .PARAMETER Identity The email or GroupID of the target group to add members to .PARAMETER Member The list of user and/or group emails that you would like to add to the target group .PARAMETER Role The role that you would like to add the members as Defaults to "MEMBER" .EXAMPLE Add-GSGroupMember "admins@domain.com" -Member "joe-admin@domain.com","sally.admin@domain.com" Adds 2 users to the group "admins@domain.com" #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias('GroupEmail','Group','Email')] [String] $Identity, [parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true,Position = 1)] [Alias("PrimaryEmail","UserKey","Mail","User","UserEmail")] [ValidateNotNullOrEmpty()] [String[]] $Member, [parameter(Mandatory = $false)] [ValidateSet("MEMBER","MANAGER","OWNER")] [String] $Role = "MEMBER" ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.group' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { try { if ($Identity -notlike "*@*.*") { $Identity = "$($Identity)@$($Script:PSGSuite.Domain)" } $groupObj = Get-GSGroup -Group $Identity -Verbose:$false try { foreach ($U in $Member) { if ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } Write-Verbose "Adding '$U' as a $Role of group '$Identity'" $body = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.Member' $body.Email = $U $body.Role = $Role $request = $service.Members.Insert($body,$groupObj.Id) $request.Execute() | Add-Member -MemberType NoteProperty -Name 'Group' -Value $Identity -PassThru } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Add-GSGroupMember' function Add-GSPrincipalGroupMembership { <# .SYNOPSIS Adds the target email to a list of groups .DESCRIPTION Adds the target email to a list of groups. Designed for parity with Add-ADPrincipalGroupMembership .PARAMETER Identity The user or group email that you would like to add to the list of groups .PARAMETER MemberOf The list of groups to add the target email to .PARAMETER Role The role that you would like to add the members as Defaults to "MEMBER" .EXAMPLE Add-GSPrincipalGroupMembership "joe@domain.com" -MemberOf "admins@domain.com","users@domain.com" Adds the email "joe@domain.com" to the admins@ and users@ groups #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail","User","Email","UserEmail")] [String] $Identity, [parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true,Position = 1)] [Alias('GroupEmail','Group')] [ValidateNotNullOrEmpty()] [String[]] $MemberOf, [parameter(Mandatory = $false)] [ValidateSet("MEMBER","MANAGER","OWNER")] [String] $Role = "MEMBER" ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.group' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { try { if ($Identity -notlike "*@*.*") { $Identity = "$($Identity)@$($Script:PSGSuite.Domain)" } try { foreach ($U in $MemberOf) { $groupObj = Get-GSGroup -Group $U -Verbose:$false if ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } Write-Verbose "Adding '$Identity' as a $Role of group '$U'" $body = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.Member' $body.Email = $Identity $body.Role = $Role $request = $service.Members.Insert($body,$groupObj.Id) $request.Execute() | Add-Member -MemberType NoteProperty -Name 'Group' -Value $U -PassThru } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Add-GSPrincipalGroupMembership' function Get-GSGroup { <# .SYNOPSIS Gets the specified group's information. Returns the full group list if -Group is excluded .DESCRIPTION Gets the specified group's information. Returns the full group list if -Group is excluded. Designed for parity with Get-ADGroup (although Google's API is unable to 'Filter' for groups) .PARAMETER Group The list of groups you would like to retrieve info for. If excluded, returns the group list instead .PARAMETER Filter Query string search. Complete documentation is at https://developers.google.com/admin-sdk/directory/v1/guides/search-groups .PARAMETER Where_IsAMember Include a user email here to get the list of groups that user is a member of .PARAMETER Domain The domain name. Use this field to get fields from only one domain. To return groups for all domains you own, exclude this parameter .PARAMETER Fields The fields to return in the response .PARAMETER PageSize Page size of the result set Defaults to 200 .EXAMPLE Get-GSGroup -Where_IsAMember "joe@domain.com" Gets the list of groups that joe@domain.com is a member of .EXAMPLE Get-GSGroup -Domain mysubdomain.org Gets the list of groups only for the 'mysubdomain.org' domain. .EXAMPLE Get-GSGroup -Filter "email:support*" Gets all the groups with emails beginning with 'support' .EXAMPLE Get-GSGroup -Filter "name -eq 'IT HelpDesk'" Gets the IT HelpDesk group by name using PowerShell syntax. PowerShell syntax is supported as a best effort, please refer to the Group Search documentation from Google for exact syntax. #> [cmdletbinding(DefaultParameterSetName = "List")] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true,ParameterSetName = "Get")] [Alias("Email")] [ValidateNotNullOrEmpty()] [String[]] $Group, [parameter(Mandatory = $false,ParameterSetName = "List")] [Alias('Query')] [string] $Filter, [parameter(Mandatory = $false,ParameterSetName = "List")] [String] $Where_IsAMember, [parameter(Mandatory = $false,ParameterSetName = "List")] [string] $Domain, [parameter(Mandatory = $false,ParameterSetName = "Get")] [String[]] $Fields, [parameter(Mandatory = $false,ParameterSetName = "List")] [ValidateRange(1,200)] [Alias("MaxResults")] [Int] $PageSize = "200" ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.group' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { switch ($PSCmdlet.ParameterSetName) { Get { foreach ($G in $Group) { try { if ($G -notlike "*@*.*") { $G = "$($G)@$($Script:PSGSuite.Domain)" } Write-Verbose "Getting group '$G'" $request = $service.Groups.Get($G) if ($Fields) { $request.Fields = "$($Fields -join ",")" } $request.Execute() | Add-Member -MemberType NoteProperty -Name 'Group' -Value $G -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } List { $verbString = "Getting all G Suite Groups" try { $request = $service.Groups.List() if ($PSBoundParameters.Keys -contains 'Where_IsAMember') { if ($Where_IsAMember -ceq "me") { $Where_IsAMember = $Script:PSGSuite.AdminEmail } elseif ($Where_IsAMember -notlike "*@*.*") { $Where_IsAMember = "$($Where_IsAMember)@$($Script:PSGSuite.Domain)" } $verbString += " where '$Where_IsAMember' is a member" $request.UserKey = $Where_IsAMember } if ($PSBoundParameters.Keys -contains 'Filter') { if ($Filter -eq '*') { $Filter = "" } else { $Filter = "$($Filter -join " ")" } $Filter = $Filter -replace " -eq ","=" -replace " -like ",":" -replace " -match ",":" -replace " -contains ",":" -creplace "'True'","True" -creplace "'False'","False" if (-not [String]::IsNullOrEmpty($Filter.Trim())) { $verbString += " matching query '$($Filter.Trim())'" $request.Query = $Filter.Trim() } } if ($PSBoundParameters.Keys -contains 'Domain') { $verbString += " for domain '$Domain'" $request.Domain = $Domain } elseif ( -not [String]::IsNullOrEmpty($Script:PSGSuite.CustomerID)) { $verbString += " for customer '$($Script:PSGSuite.CustomerID)'" $request.Customer = $Script:PSGSuite.CustomerID } else { $verbString += " for customer 'my_customer'" $request.Customer = "my_customer" } if ($PageSize) { $request.MaxResults = $PageSize } Write-Verbose $verbString [int]$i = 1 do { $result = $request.Execute() if ($null -ne $result.GroupsValue) { $result.GroupsValue | Add-Member -MemberType ScriptMethod -Name ToString -Value {$this.Email} -PassThru -Force } $request.PageToken = $result.NextPageToken [int]$retrieved = ($i + $result.GroupsValue.Count) - 1 Write-Verbose "Retrieved $retrieved groups..." [int]$i = $i + $result.GroupsValue.Count } until (!$result.NextPageToken) } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } } Export-ModuleMember -Function 'Get-GSGroup' function Get-GSGroupAlias { <# .SYNOPSIS Gets the specified G SUite Group's aliases .DESCRIPTION Gets the specified G SUite Group's aliases .PARAMETER Group The primary email or ID of the group who you are trying to get aliases for. You can exclude the '@domain.com' to insert the Domain in the config or use the special 'me' to indicate the AdminEmail in the config. .EXAMPLE Get-GSGroupAlias -Group hr Gets the list of aliases for the group hr@domain.com #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias("Email")] [ValidateNotNullOrEmpty()] [String[]] $Group ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.group' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { foreach ($G in $Group) { try { if ($G -notlike "*@*.*") { $G = "$($G)@$($Script:PSGSuite.Domain)" } Write-Verbose "Getting Alias list for Group '$G'" $request = $service.Groups.Aliases.List($G) $request.Execute() | Select-Object -ExpandProperty AliasesValue } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Get-GSGroupAlias' function Get-GSGroupMember { <# .SYNOPSIS Gets the group member list of a target group .DESCRIPTION Gets the group member list of a target group. Designed for parity with Get-ADGroupMember .PARAMETER Identity The email or GroupID of the target group .PARAMETER Member If specified, returns only the information for this member of the target group .PARAMETER Roles If specified, returns only the members of the specified role(s) .PARAMETER PageSize Page size of the result set .EXAMPLE Get-GSGroupMember "admins@domain.com" -Roles Owner,Manager Returns the list of owners and managers of the group "admins@domain.com" #> [cmdletbinding(DefaultParameterSetName = "List")] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias('GroupEmail','Group','Email')] [String[]] $Identity, [parameter(Mandatory = $false,Position = 1,ParameterSetName = "Get")] [Alias("PrimaryEmail","UserKey","Mail","User","UserEmail")] [ValidateNotNullOrEmpty()] [String[]] $Member, [parameter(Mandatory=$false,ParameterSetName = "List")] [ValidateSet("Owner","Manager","Member")] [String[]] $Roles, [parameter(Mandatory=$false,ParameterSetName = "List")] [ValidateRange(1,200)] [Int] $PageSize="200" ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.group' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { switch ($PSCmdlet.ParameterSetName) { Get { foreach ($I in $Identity) { try { if ($I -notlike "*@*.*") { $I = "$($I)@$($Script:PSGSuite.Domain)" } foreach ($G in $Member) { if ($G -notlike "*@*.*") { $G = "$($G)@$($Script:PSGSuite.Domain)" } Write-Verbose "Getting member '$G' of group '$I'" $request = $service.Members.Get($I,$G) $request.Execute() | Add-Member -MemberType NoteProperty -Name 'Group' -Value $I -PassThru } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } List { foreach ($Id in $Identity) { try { if ($Id -notlike "*@*.*") { $Id = "$($Id)@$($Script:PSGSuite.Domain)" } $request = $service.Members.List($Id) if ($PageSize) { $request.MaxResults = $PageSize } if ($Roles) { Write-Verbose "Getting all members of group '$Id' in the following role(s): $($Roles -join ',')" $request.Roles = "$($Roles -join ',')" } else { Write-Verbose "Getting all members of group '$Id'" } [int]$i = 1 do { $result = $request.Execute() if ($null -ne $result.MembersValue) { $result.MembersValue | Add-Member -MemberType NoteProperty -Name 'Group' -Value $Id -PassThru | Add-Member -MemberType ScriptMethod -Name ToString -Value {$this.Email} -PassThru -Force } $request.PageToken = $result.NextPageToken [int]$retrieved = ($i + $result.MembersValue.Count) - 1 Write-Verbose "Retrieved $retrieved members..." [int]$i = $i + $result.MembersValue.Count } until (!$result.NextPageToken) } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } } } Export-ModuleMember -Function 'Get-GSGroupMember' function Get-GSGroupSettings { <# .SYNOPSIS Gets a group's settings .DESCRIPTION Gets a group's settings .PARAMETER Identity The email of the group If only the email name-part is passed, the full email will be contstructed using the Domain from the active config .EXAMPLE Get-GSGroupSettings admins Gets the group settings for admins@domain.com #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias('GroupEmail','Group','Email')] [String[]] $Identity ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/apps.groups.settings' ServiceType = 'Google.Apis.Groupssettings.v1.GroupssettingsService' } $service = New-GoogleService @serviceParams } Process { try { foreach ($G in $Identity) { if ($G -notlike "*@*.*") { $G = "$($G)@$($Script:PSGSuite.Domain)" } Write-Verbose "Getting settings for group '$G'" $request = $service.Groups.Get($G) $request.Alt = "Json" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'Group' -Value $G -PassThru } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSGroupSettings' function New-GSGroup { <# .SYNOPSIS Creates a new Google Group .DESCRIPTION Creates a new Google Group .PARAMETER Email The desired email of the new group. If the group already exists, a GoogleApiException will be thrown. You can exclude the '@domain.com' to insert the Domain in the config .PARAMETER Name The name of the new group .PARAMETER Description The description of the new group .EXAMPLE New-GSGroup -Email appdev -Name "Application Developers" -Description "App Dev team members" Creates a new group named "Application Developers" with the email "appdev@domain.com" and description "App Dev team members" #> [cmdletbinding()] Param ( [parameter(Mandatory = $true)] [String] $Email, [parameter(Mandatory = $true)] [String] $Name, [parameter(Mandatory = $false)] [String] $Description ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.group' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { try { if ($Email -notlike "*@*.*") { $Email = "$($Email)@$($Script:PSGSuite.Domain)" } Write-Verbose "Creating group '$Email'" $body = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.Group' foreach ($prop in $PSBoundParameters.Keys | Where-Object {$body.PSObject.Properties.Name -contains $_}) { switch ($prop) { Email { if ($PSBoundParameters[$prop] -notlike "*@*.*") { $PSBoundParameters[$prop] = "$($PSBoundParameters[$prop])@$($Script:PSGSuite.Domain)" } $body.$prop = $PSBoundParameters[$prop] } Default { $body.$prop = $PSBoundParameters[$prop] } } } $request = $service.Groups.Insert($body) $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'New-GSGroup' function New-GSGroupAlias { <# .SYNOPSIS Creates a new alias for a G Suite group .DESCRIPTION Creates a new alias for a G Suite group .PARAMETER Group The group to create the alias for .PARAMETER Alias The alias or list of aliases to create for the group .EXAMPLE New-GSGroupAlias -Group humanresources@domain.com -Alias 'hr@domain.com','hrhelp@domain.com' Creates 2 new aliases for group Human Resources as 'hr@domain.com' and 'hrhelp@domain.com' #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias("Email")] [ValidateNotNullOrEmpty()] [String] $Group, [parameter(Mandatory = $true,Position = 1)] [String[]] $Alias ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.group' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { foreach ($A in $Alias) { try { if ($Group -notlike "*@*.*") { $Group = "$($Group)@$($Script:PSGSuite.Domain)" } if ($A -notlike "*@*.*") { $A = "$($A)@$($Script:PSGSuite.Domain)" } Write-Verbose "Creating alias '$A' for Group '$Group'" $body = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.Alias' $body.AliasValue = $A $request = $service.Groups.Aliases.Insert($body,$Group) $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'New-GSGroupAlias' function Remove-GSGroup { <# .SYNOPSIS Removes a group .DESCRIPTION Removes a group .PARAMETER Identity The email or unique Id of the group to removed .EXAMPLE Remove-GSGroup 'test_group' -Confirm:$false Removes the group 'test_group@domain.com' without asking for confirmation #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias('GroupEmail','Group','Email')] [String[]] $Identity ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.group' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { try { foreach ($G in $Identity) { if ($G -notlike "*@*.*") { $G = "$($G)@$($Script:PSGSuite.Domain)" } if ($PSCmdlet.ShouldProcess("Removing group '$G'")) { Write-Verbose "Removing group '$G'" $request = $service.Groups.Delete($G) $request.Execute() Write-Verbose "Group '$G' has been successfully removed" } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Remove-GSGroup' function Remove-GSGroupAlias { <# .SYNOPSIS Removes an alias from a G Suite group .DESCRIPTION Removes an alias from a G Suite group .PARAMETER Group The group to remove the alias from .PARAMETER Alias The alias or list of aliases to remove from the group .EXAMPLE Remove-GSGroupAlias -Group humanresources@domain.com -Alias 'hr@domain.com','hrhelp@domain.com' Removes 2 aliases for group Human Resources: 'hr@domain.com' and 'hrhelp@domain.com' #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias("Email")] [ValidateNotNullOrEmpty()] [String] $Group, [parameter(Mandatory = $true,Position = 1)] [String[]] $Alias ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.group' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { foreach ($A in $Alias) { try { if ($Group -notlike "*@*.*") { $Group = "$($Group)@$($Script:PSGSuite.Domain)" } if ($A -notlike "*@*.*") { $A = "$($A)@$($Script:PSGSuite.Domain)" } if ($PSCmdlet.ShouldProcess("Removing alias '$A' from Group '$Group'")) { Write-Verbose "Removing alias '$A' from Group '$Group'" $request = $service.Groups.Aliases.Delete($Group,$A) $request.Execute() Write-Verbose "Alias '$A' has been successfully deleted from Group '$Group'" } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Remove-GSGroupAlias' function Remove-GSGroupMember { <# .SYNOPSIS Removes members from a group .DESCRIPTION Removes members from a group .PARAMETER Identity The email or unique Id of the group to remove members from .PARAMETER Member The member or array of members to remove from the target group .EXAMPLE Remove-GSGroupMember -Identity admins -Member joe.smith,mark.taylor -Confirm:$false Removes members Joe Smith and Mark Taylor from the group admins@domain.com and skips asking for confirmation #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias('GroupEmail','Group','Email')] [String] $Identity, [parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true,Position = 1)] [Alias("PrimaryEmail","UserKey","Mail","User","UserEmail")] [ValidateNotNullOrEmpty()] [String[]] $Member ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.group' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { if ($Identity -notlike "*@*.*") { $Identity = "$($Identity)@$($Script:PSGSuite.Domain)" } foreach ($G in $Member) { try { if ($G -notlike "*@*.*") { $G = "$($G)@$($Script:PSGSuite.Domain)" } if ($PSCmdlet.ShouldProcess("Removing member '$G' from group '$Identity'")) { Write-Verbose "Removing member '$G' from group '$Identity'" $request = $service.Members.Delete($Identity,$G) $request.Execute() Write-Verbose "Member '$G' has been successfully removed" } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Remove-GSGroupMember' function Remove-GSPrincipalGroupMembership { <# .SYNOPSIS Removes the target member from a group or list of groups .DESCRIPTION Removes the target member from a group or list of groups .PARAMETER Identity The email or unique Id of the member you would like to remove from the group(s) .PARAMETER MemberOf The group(s) to remove the member from .EXAMPLE Remove-GSPrincipalGroupMembership -Identity 'joe.smith' -MemberOf admins,test_pool Removes Joe Smith from the groups admins@domain.com and test_pool@domain.com #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail","User","UserEmail")] [String] $Identity, [parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true,Position = 1)] [Alias('GroupEmail','Group','Email')] [String[]] $MemberOf ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.group' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { if ($Identity -notlike "*@*.*") { $Identity = "$($Identity)@$($Script:PSGSuite.Domain)" } foreach ($G in $MemberOf) { try { if ($G -notlike "*@*.*") { $G = "$($G)@$($Script:PSGSuite.Domain)" } if ($PSCmdlet.ShouldProcess("Removing member '$Identity' from group '$G'")) { Write-Verbose "Removing member '$Identity' from group '$G'" $request = $service.Members.Delete($G,$Identity) $request.Execute() Write-Verbose "Member '$G' has been successfully removed" } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Remove-GSPrincipalGroupMembership' function Set-GSGroupSettings { <# .SYNOPSIS Hard-sets the settings for a group .DESCRIPTION Hard-sets the settings for a group .PARAMETER Identity The primary email or unique Id of the group to set .PARAMETER Name The new name of the group .PARAMETER Description The new description of the group .PARAMETER ArchiveOnly If the group is archive only .PARAMETER AllowExternalMembers Are external members allowed to join the group .PARAMETER AllowGoogleCommunication Is google allowed to contact admins .PARAMETER AllowWebPosting If posting from web is allowed .PARAMETER CustomFooterText Custom footer text .PARAMETER CustomReplyToAddress Default email to which reply to any message should go .PARAMETER DefaultMessageDenyNotificationText Default message deny notification message .PARAMETER Email Email id of the group .PARAMETER IncludeCustomFooter Whether to include custom footer .PARAMETER IncludeInGlobalAddressList If this groups should be included in global address list or not .PARAMETER IsArchived If the contents of the group are archived .PARAMETER MaxMessageBytes Maximum message size allowed .PARAMETER MembersCanPostAsTheGroup Can members post using the group email address .PARAMETER MessageDisplayFont Default message display font Available values are: * "DEFAULT_FONT" * "FIXED_WIDTH_FONT" .PARAMETER MessageModerationLevel Moderation level for messages Available values are: * "MODERATE_ALL_MESSAGES" * "MODERATE_NON_MEMBERS" * "MODERATE_NEW_MEMBERS" * "MODERATE_NONE" .PARAMETER ReplyTo Who should the default reply to a message go to Available values are: * "REPLY_TO_CUSTOM" * "REPLY_TO_SENDER" * "REPLY_TO_LIST" * "REPLY_TO_OWNER" * "REPLY_TO_IGNORE" * "REPLY_TO_MANAGERS" .PARAMETER SendMessageDenyNotification Should the member be notified if his message is denied by owner .PARAMETER ShowInGroupDirectory Is the group listed in groups directory .PARAMETER SpamModerationLevel Moderation level for messages detected as spam Available values are: * "ALLOW" * "MODERATE" * "SILENTLY_MODERATE" * "REJECT" .PARAMETER WhoCanAdd Permissions to add members Available values are: * "ALL_MANAGERS_CAN_ADD" * "ALL_MEMBERS_CAN_ADD" * "NONE_CAN_ADD" .PARAMETER WhoCanContactOwner Permission to contact owner of the group via web UI Available values are: * "ANYONE_CAN_CONTACT" * "ALL_IN_DOMAIN_CAN_CONTACT" * "ALL_MEMBERS_CAN_CONTACT" * "ALL_MANAGERS_CAN_CONTACT" .PARAMETER WhoCanInvite Permissions to invite members. Available values are: * "ALL_MEMBERS_CAN_INVITE" * "ALL_MANAGERS_CAN_INVITE" * "NONE_CAN_INVITE" .PARAMETER WhoCanJoin Permissions to join the group. Available values are: * "ANYONE_CAN_JOIN" * "ALL_IN_DOMAIN_CAN_JOIN" * "INVITED_CAN_JOIN" * "CAN_REQUEST_TO_JOIN" .PARAMETER WhoCanLeaveGroup Permission to leave the group. Available values are: * "ALL_MANAGERS_CAN_LEAVE" * "ALL_MEMBERS_CAN_LEAVE" * "NONE_CAN_LEAVE" .PARAMETER WhoCanPostMessage Permissions to post messages to the group. Available values are: * "NONE_CAN_POST" * "ALL_MANAGERS_CAN_POST" * "ALL_MEMBERS_CAN_POST" * "ALL_OWNERS_CAN_POST" * "ALL_IN_DOMAIN_CAN_POST" * "ANYONE_CAN_POST" .PARAMETER WhoCanViewGroup Permissions to view group. Available values are: * "ANYONE_CAN_VIEW" * "ALL_IN_DOMAIN_CAN_VIEW" * "ALL_MEMBERS_CAN_VIEW" * "ALL_MANAGERS_CAN_VIEW" .PARAMETER WhoCanViewMembership Permissions to view membership. Available values are: * "ALL_IN_DOMAIN_CAN_VIEW" * "ALL_MEMBERS_CAN_VIEW" * "ALL_MANAGERS_CAN_VIEW" .EXAMPLE Set-GSGroupSettings admins,hr-notifications -AllowExternalMembers:$false -WhoCanPostMessage ALL_OWNERS_CAN_POST Sets the group settings for both admins@domain.com and hr-notifications@domain.com to deny external members and limit posting to only group owners #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias('GroupEmail','Group')] [String[]] $Identity, [parameter(Mandatory = $false)] [String] $Name, [parameter(Mandatory = $false)] [String] $Description, [parameter(Mandatory = $false)] [Switch] $ArchiveOnly, [parameter(Mandatory = $false)] [Switch] $AllowExternalMembers, [parameter(Mandatory = $false)] [Switch] $AllowGoogleCommunication, [parameter(Mandatory = $false)] [Switch] $AllowWebPosting, [parameter(Mandatory = $false)] [ValidateScript( {$_.length -le 1000})] [String] $CustomFooterText, [parameter(Mandatory = $false)] [String] $CustomReplyToAddress, [parameter(Mandatory = $false)] [Alias('MessageDenyNotificationText')] [ValidateScript( {$_.length -le 10000})] [String] $DefaultMessageDenyNotificationText, [parameter(Mandatory = $false)] [String] $Email, [parameter(Mandatory = $false)] [Switch] $IncludeCustomFooter, [parameter(Mandatory = $false)] [Switch] $IncludeInGlobalAddressList, [parameter(Mandatory = $false)] [Switch] $IsArchived, [parameter(Mandatory = $false)] [int] $MaxMessageBytes, [parameter(Mandatory = $false)] [Switch] $MembersCanPostAsTheGroup, [parameter(Mandatory = $false)] [ValidateSet("DEFAULT_FONT","FIXED_WIDTH_FONT")] [String] $MessageDisplayFont, [parameter(Mandatory = $false)] [ValidateSet("MODERATE_ALL_MESSAGES","MODERATE_NEW_MEMBERS","MODERATE_NONE","MODERATE_NON_MEMBERS")] [String] $MessageModerationLevel, [parameter(Mandatory = $false)] [ValidateSet("REPLY_TO_CUSTOM","REPLY_TO_IGNORE","REPLY_TO_LIST","REPLY_TO_MANAGERS","REPLY_TO_OWNER","REPLY_TO_SENDER")] [String] $ReplyTo, [parameter(Mandatory = $false)] [Switch] $SendMessageDenyNotification, [parameter(Mandatory = $false)] [Switch] $ShowInGroupDirectory, [parameter(Mandatory = $false)] [ValidateSet("ALLOW","MODERATE","SILENTLY_MODERATE","REJECT")] [String] $SpamModerationLevel, [parameter(Mandatory = $false)] [ValidateSet("ALL_MEMBERS_CAN_ADD","ALL_MANAGERS_CAN_ADD","NONE_CAN_ADD")] [String] $WhoCanAdd, [parameter(Mandatory = $false)] [ValidateSet("ALL_IN_DOMAIN_CAN_CONTACT","ALL_MANAGERS_CAN_CONTACT","ALL_MEMBERS_CAN_CONTACT","ANYONE_CAN_CONTACT")] [String] $WhoCanContactOwner, [parameter(Mandatory = $false)] [ValidateSet("ALL_MANAGERS_CAN_INVITE","ALL_MEMBERS_CAN_INVITE","NONE_CAN_INVITE")] [String] $WhoCanInvite, [parameter(Mandatory = $false)] [ValidateSet("ALL_IN_DOMAIN_CAN_JOIN","ANYONE_CAN_JOIN","CAN_REQUEST_TO_JOIN","INVITED_CAN_JOIN")] [String] $WhoCanJoin, [parameter(Mandatory = $false)] [ValidateSet("ALL_MANAGERS_CAN_LEAVE","ALL_MEMBERS_CAN_LEAVE","NONE_CAN_LEAVE")] [String] $WhoCanLeaveGroup, [parameter(Mandatory = $false)] [ValidateSet("ALL_IN_DOMAIN_CAN_POST","ALL_MANAGERS_CAN_POST","ALL_MEMBERS_CAN_POST","ANYONE_CAN_POST","NONE_CAN_POST")] [String] $WhoCanPostMessage, [parameter(Mandatory = $false)] [ValidateSet("ALL_IN_DOMAIN_CAN_VIEW","ALL_MANAGERS_CAN_VIEW","ALL_MEMBERS_CAN_VIEW","ANYONE_CAN_VIEW")] [String] $WhoCanViewGroup, [parameter(Mandatory = $false)] [ValidateSet("ALL_IN_DOMAIN_CAN_VIEW","ALL_MANAGERS_CAN_VIEW","ALL_MEMBERS_CAN_VIEW")] [String] $WhoCanViewMembership ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/apps.groups.settings' ServiceType = 'Google.Apis.Groupssettings.v1.GroupssettingsService' } $service = New-GoogleService @serviceParams } Process { try { foreach ($G in $Identity) { if ($G -notlike "*@*.*") { $G = "$($G)@$($Script:PSGSuite.Domain)" } Write-Verbose "Updating settings for group '$G'" $body = New-Object 'Google.Apis.Groupssettings.v1.Data.Groups' foreach ($prop in $PSBoundParameters.Keys | Where-Object {$body.PSObject.Properties.Name -contains $_}) { switch ($prop) { MaxMessageBytes { $body.$prop = $PSBoundParameters[$prop] } Default { $body.$prop = if ($PSBoundParameters[$prop].ToString() -in @("True","False")) { $($PSBoundParameters[$prop]).ToString().ToLower() } else { $PSBoundParameters[$prop] } } } } $request = $service.Groups.Update($body,$G) $request.Alt = "Json" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'Group' -Value $G -PassThru } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Set-GSGroupSettings' function Update-GSGroupMember { <# .SYNOPSIS Updates a group member's role and/or delivery preference .DESCRIPTION Updates a group member's role and/or delivery preference .PARAMETER GroupEmail The email or GroupID of the group to update members of .PARAMETER Member The member email or list of member emails that you would like to update .PARAMETER Role The role that you would like to update the members to Acceptable values are: * MEMBER * MANAGER * OWNER .PARAMETER DeliverySettings Defines mail delivery preferences of member Acceptable values are: * "ALL_MAIL": All messages, delivered as soon as they arrive. * "DAILY": No more than one message a day. * "DIGEST": Up to 25 messages bundled into a single message. * "DISABLED": Remove subscription. * "NONE": No messages. .EXAMPLE Get-GSGroupMember myGroup | Update-GSGroupMember -DeliverySettings ALL_MAIL Updates the delivery preference for all members of group 'myGroup@domain.com' to 'ALL_MAIL' #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias('Group')] [String] $GroupEmail, [parameter(Mandatory = $true,Position = 1,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail","User","UserEmail","Email")] [ValidateNotNullOrEmpty()] [String[]] $Member, [parameter(Mandatory = $false)] [ValidateSet("MEMBER","MANAGER","OWNER")] [String] $Role, [parameter(Mandatory = $false)] [ValidateSet("ALL_MAIL","DAILY","DIGEST","DISABLED","NONE")] [String] $DeliverySettings ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.group' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { try { if ($GroupEmail -notlike "*@*.*") { $GroupEmail = "$($GroupEmail)@$($Script:PSGSuite.Domain)" } #$groupObj = Get-GSGroup -Group $Identity -Verbose:$false try { foreach ($U in $Member) { if ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } Write-Verbose "Updating member '$U' of group '$GroupEmail'" $body = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.Member' if ($PSBoundParameters.Keys -contains 'DeliverySettings') { $body.DeliverySettings = $PSBoundParameters['DeliverySettings'] } if ($PSBoundParameters.Keys -contains 'Role') { $body.Role = $PSBoundParameters['Role'] } $request = $service.Members.Patch($body,$GroupEmail,$U) $request.Execute() | Add-Member -MemberType NoteProperty -Name 'Group' -Value $GroupEmail -PassThru } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Update-GSGroupMember' function Update-GSGroupSettings { <# .SYNOPSIS Updates the settings for a group while retaining excluded, already existing settings .DESCRIPTION Updates the settings for a group while retaining excluded, already existing settings .PARAMETER Identity The primary email or unique Id of the group to update .PARAMETER Name The new name of the group .PARAMETER Description The new description of the group .PARAMETER ArchiveOnly If the group is archive only .PARAMETER AllowExternalMembers Are external members allowed to join the group .PARAMETER AllowGoogleCommunication Is google allowed to contact admins .PARAMETER AllowWebPosting If posting from web is allowed .PARAMETER CustomFooterText Custom footer text .PARAMETER CustomReplyToAddress Default email to which reply to any message should go .PARAMETER DefaultMessageDenyNotificationText Default message deny notification message .PARAMETER Email Email id of the group .PARAMETER IncludeCustomFooter Whether to include custom footer .PARAMETER IncludeInGlobalAddressList If this groups should be included in global address list or not .PARAMETER IsArchived If the contents of the group are archived .PARAMETER MaxMessageBytes Maximum message size allowed .PARAMETER MembersCanPostAsTheGroup Can members post using the group email address .PARAMETER MessageDisplayFont Default message display font Available values are: * "DEFAULT_FONT" * "FIXED_WIDTH_FONT" .PARAMETER MessageModerationLevel Moderation level for messages Available values are: * "MODERATE_ALL_MESSAGES" * "MODERATE_NON_MEMBERS" * "MODERATE_NEW_MEMBERS" * "MODERATE_NONE" .PARAMETER ReplyTo Who should the default reply to a message go to Available values are: * "REPLY_TO_CUSTOM" * "REPLY_TO_SENDER" * "REPLY_TO_LIST" * "REPLY_TO_OWNER" * "REPLY_TO_IGNORE" * "REPLY_TO_MANAGERS" .PARAMETER SendMessageDenyNotification Should the member be notified if his message is denied by owner .PARAMETER ShowInGroupDirectory Is the group listed in groups directory .PARAMETER SpamModerationLevel Moderation level for messages detected as spam Available values are: * "ALLOW" * "MODERATE" * "SILENTLY_MODERATE" * "REJECT" .PARAMETER WhoCanAdd Permissions to add members Available values are: * "ALL_MANAGERS_CAN_ADD" * "ALL_MEMBERS_CAN_ADD" * "NONE_CAN_ADD" .PARAMETER WhoCanContactOwner Permission to contact owner of the group via web UI Available values are: * "ANYONE_CAN_CONTACT" * "ALL_IN_DOMAIN_CAN_CONTACT" * "ALL_MEMBERS_CAN_CONTACT" * "ALL_MANAGERS_CAN_CONTACT" .PARAMETER WhoCanInvite Permissions to invite members. Available values are: * "ALL_MEMBERS_CAN_INVITE" * "ALL_MANAGERS_CAN_INVITE" * "NONE_CAN_INVITE" .PARAMETER WhoCanJoin Permissions to join the group. Available values are: * "ANYONE_CAN_JOIN" * "ALL_IN_DOMAIN_CAN_JOIN" * "INVITED_CAN_JOIN" * "CAN_REQUEST_TO_JOIN" .PARAMETER WhoCanLeaveGroup Permission to leave the group. Available values are: * "ALL_MANAGERS_CAN_LEAVE" * "ALL_MEMBERS_CAN_LEAVE" * "NONE_CAN_LEAVE" .PARAMETER WhoCanPostMessage Permissions to post messages to the group. Available values are: * "NONE_CAN_POST" * "ALL_MANAGERS_CAN_POST" * "ALL_MEMBERS_CAN_POST" * "ALL_OWNERS_CAN_POST" * "ALL_IN_DOMAIN_CAN_POST" * "ANYONE_CAN_POST" .PARAMETER WhoCanViewGroup Permissions to view group. Available values are: * "ANYONE_CAN_VIEW" * "ALL_IN_DOMAIN_CAN_VIEW" * "ALL_MEMBERS_CAN_VIEW" * "ALL_MANAGERS_CAN_VIEW" .PARAMETER WhoCanViewMembership Permissions to view membership. Available values are: * "ALL_IN_DOMAIN_CAN_VIEW" * "ALL_MEMBERS_CAN_VIEW" * "ALL_MANAGERS_CAN_VIEW" .EXAMPLE Updates-GSGroupSettings admins,hr-notifications -AllowExternalMembers:$false -WhoCanPostMessage ALL_OWNERS_CAN_POST Updates the group settings for both admins@domain.com and hr-notifications@domain.com to deny external members and limit posting to only group owners #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias('GroupEmail','Group')] [String[]] $Identity, [parameter(Mandatory = $false)] [String] $Name, [parameter(Mandatory = $false)] [String] $Description, [parameter(Mandatory = $false)] [Switch] $ArchiveOnly, [parameter(Mandatory = $false)] [Switch] $AllowExternalMembers, [parameter(Mandatory = $false)] [Switch] $AllowGoogleCommunication, [parameter(Mandatory = $false)] [Switch] $AllowWebPosting, [parameter(Mandatory = $false)] [ValidateScript( {$_.length -le 1000})] [String] $CustomFooterText, [parameter(Mandatory = $false)] [String] $CustomReplyToAddress, [parameter(Mandatory = $false)] [Alias('MessageDenyNotificationText')] [ValidateScript( {$_.length -le 10000})] [String] $DefaultMessageDenyNotificationText, [parameter(Mandatory = $false)] [String] $Email, [parameter(Mandatory = $false)] [Switch] $IncludeCustomFooter, [parameter(Mandatory = $false)] [Switch] $IncludeInGlobalAddressList, [parameter(Mandatory = $false)] [Switch] $IsArchived, [parameter(Mandatory = $false)] [int] $MaxMessageBytes, [parameter(Mandatory = $false)] [Switch] $MembersCanPostAsTheGroup, [parameter(Mandatory = $false)] [ValidateSet("DEFAULT_FONT","FIXED_WIDTH_FONT")] [String] $MessageDisplayFont, [parameter(Mandatory = $false)] [ValidateSet("MODERATE_ALL_MESSAGES","MODERATE_NEW_MEMBERS","MODERATE_NONE","MODERATE_NON_MEMBERS")] [String] $MessageModerationLevel, [parameter(Mandatory = $false)] [ValidateSet("REPLY_TO_CUSTOM","REPLY_TO_IGNORE","REPLY_TO_LIST","REPLY_TO_MANAGERS","REPLY_TO_OWNER","REPLY_TO_SENDER")] [String] $ReplyTo, [parameter(Mandatory = $false)] [Switch] $SendMessageDenyNotification, [parameter(Mandatory = $false)] [Switch] $ShowInGroupDirectory, [parameter(Mandatory = $false)] [ValidateSet("ALLOW","MODERATE","SILENTLY_MODERATE","REJECT")] [String] $SpamModerationLevel, [parameter(Mandatory = $false)] [ValidateSet("ALL_MEMBERS_CAN_ADD","ALL_MANAGERS_CAN_ADD","NONE_CAN_ADD")] [String] $WhoCanAdd, [parameter(Mandatory = $false)] [ValidateSet("ALL_IN_DOMAIN_CAN_CONTACT","ALL_MANAGERS_CAN_CONTACT","ALL_MEMBERS_CAN_CONTACT","ANYONE_CAN_CONTACT")] [String] $WhoCanContactOwner, [parameter(Mandatory = $false)] [ValidateSet("ALL_MANAGERS_CAN_INVITE","ALL_MEMBERS_CAN_INVITE","NONE_CAN_INVITE")] [String] $WhoCanInvite, [parameter(Mandatory = $false)] [ValidateSet("ALL_IN_DOMAIN_CAN_JOIN","ANYONE_CAN_JOIN","CAN_REQUEST_TO_JOIN","INVITED_CAN_JOIN")] [String] $WhoCanJoin, [parameter(Mandatory = $false)] [ValidateSet("ALL_MANAGERS_CAN_LEAVE","ALL_MEMBERS_CAN_LEAVE","NONE_CAN_LEAVE")] [String] $WhoCanLeaveGroup, [parameter(Mandatory = $false)] [ValidateSet("ALL_IN_DOMAIN_CAN_POST","ALL_MANAGERS_CAN_POST","ALL_MEMBERS_CAN_POST","ANYONE_CAN_POST","NONE_CAN_POST")] [String] $WhoCanPostMessage, [parameter(Mandatory = $false)] [ValidateSet("ALL_IN_DOMAIN_CAN_VIEW","ALL_MANAGERS_CAN_VIEW","ALL_MEMBERS_CAN_VIEW","ANYONE_CAN_VIEW")] [String] $WhoCanViewGroup, [parameter(Mandatory = $false)] [ValidateSet("ALL_IN_DOMAIN_CAN_VIEW","ALL_MANAGERS_CAN_VIEW","ALL_MEMBERS_CAN_VIEW")] [String] $WhoCanViewMembership ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/apps.groups.settings' ServiceType = 'Google.Apis.Groupssettings.v1.GroupssettingsService' } $service = New-GoogleService @serviceParams } Process { try { foreach ($G in $Identity) { if ($G -notlike "*@*.*") { $G = "$($G)@$($Script:PSGSuite.Domain)" } Write-Verbose "Updating settings for group '$G'" $body = New-Object 'Google.Apis.Groupssettings.v1.Data.Groups' foreach ($prop in $PSBoundParameters.Keys | Where-Object {$body.PSObject.Properties.Name -contains $_}) { switch ($prop) { MaxMessageBytes { $body.$prop = $PSBoundParameters[$prop] } Default { $body.$prop = if ($PSBoundParameters[$prop].ToString() -in @("True","False")) { $($PSBoundParameters[$prop]).ToString().ToLower() } else { $PSBoundParameters[$prop] } } } } $request = $service.Groups.Patch($body,$G) $request.Alt = "Json" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'Group' -Value $G -PassThru } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Update-GSGroupSettings' function Add-GSChatButton { <# .SYNOPSIS Creates a Chat Button widget to include in a section .DESCRIPTION Creates a Chat Button widget to include in a section .PARAMETER Text The Text for a Text Button .PARAMETER Icon The icon for the Image Button Available values are: * AIRPLANE * BOOKMARK * BUS * CAR * CLOCK * CONFIRMATION_NUMBER_ICON * DOLLAR * DESCRIPTION * EVENT_PERFORMER * EVENT_SEAT * FLIGHT_ARRIVAL * FLIGHT_DEPARTURE * HOTEL * HOTEL_ROOM_TYPE * INVITE * MAP_PIN * MEMBERSHIP * MULTIPLE_PEOPLE * OFFER * PERSON * PHONE * RESTAURANT_ICON * SHOPPING_CART * STAR * STORE * TICKET * TRAIN * VIDEO_CAMERA * VIDEO_PLAY .PARAMETER IconUrl The Url of the icon for the Image Button .PARAMETER OnClick The OnClick event that triggers when a user clicks the KeyValue You must use the function `Add-GSChatOnClick` to create OnClicks, otherwise this will throw a terminating error. .PARAMETER MessageSegment Any Chat message segment objects created with functions named `Add-GSChat*` passed through the pipeline or added directly to this parameter as values. .EXAMPLE Send-GSChatMessage -Text "Post job report:" -Cards $cards -Webhook (Get-GSChatWebhook JobReports) Sends a simple Chat message using the JobReports webhook .EXAMPLE Add-GSChatTextParagraph -Text "Guys...","We <b>NEED</b> to <i>stop</i> spending money on <b>crap</b>!" | Add-GSChatKeyValue -TopLabel "Chocolate Budget" -Content '$5.00' -Icon DOLLAR | Add-GSChatKeyValue -TopLabel "Actual Spending" -Content '$5,000,000!' -BottomLabel "WTF" -Icon AIRPLANE | Add-GSChatImage -ImageUrl "https://media.tenor.com/images/f78545a9b520ecf953578b4be220f26d/tenor.gif" -LinkImage | Add-GSChatCardSection -SectionHeader "Dollar bills, y'all" -OutVariable sect1 | Add-GSChatButton -Text "Launch nuke" -OnClick (Add-GSChatOnClick -Url "https://github.com/scrthq/PSGSuite") -Verbose -OutVariable button1 | Add-GSChatButton -Text "Unleash hounds" -OnClick (Add-GSChatOnClick -Url "https://admin.google.com/?hl=en&authuser=0") -Verbose -OutVariable button2 | Add-GSChatCardSection -SectionHeader "What should we do?" -OutVariable sect2 | Add-GSChatCard -HeaderTitle "Makin' moves with" -HeaderSubtitle "DEM GOODIES" -OutVariable card | Add-GSChatTextParagraph -Text "This message sent by <b>PSGSuite</b> via WebHook!" | Add-GSChatCardSection -SectionHeader "Additional Info" -OutVariable sect2 | Send-GSChatMessage -Text "Got that report, boss:" -FallbackText "Mistakes have been made..." -Webhook ReportRoom This example shows the pipeline capabilities of the Chat functions in PSGSuite. Starting from top to bottom: 1. Add a TextParagraph widget 2. Add a KeyValue with an icon 3. Add another KeyValue with a different icon 4. Add an image and create an OnClick event to open the image's URL by using the -LinkImage parameter 5. Add a new section to encapsulate the widgets sent through the pipeline before it 6. Add a TextButton that opens the PSGSuite GitHub repo when clicked 7. Add another TextButton that opens Google Admin Console when clicked 8. Wrap the 2 buttons in a new Section to divide the content 9. Wrap all widgets and sections in the pipeline so far in a Card 10. Add a new TextParagraph as a footer to the message 11. Wrap that TextParagraph in a new section 12. Send the message and include FallbackText that's displayed in the mobile notification. Since the final TextParagraph and Section are not followed by a new Card addition, Send-GSChatMessage will create a new Card just for the remaining segments then send the completed message via Webhook. The Webhook short-name is used to reference the full URL stored in the encrypted Config so it's not displayed in the actual script. .EXAMPLE Get-Service | Select-Object -First 5 | ForEach-Object { Add-GSChatKeyValue -TopLabel $_.DisplayName -Content $_.Status -BottomLabel $_.Name -Icon TICKET } | Add-GSChatCardSection -SectionHeader "Top 5 Services" | Send-GSChatMessage -Text "Service Report:" -FallbackText "Service Report" -Webhook Reports This gets the first 5 Services returned by Get-Service, creates KeyValue widgets for each, wraps it in a section with a header, then sends it to the Reports Webhook #> [CmdletBinding(DefaultParameterSetName = "Text")] Param ( [parameter(Mandatory = $true,Position = 0,ParameterSetName = "Text")] [String] $Text, [parameter(Mandatory = $true,ParameterSetName = "Icon")] [ValidateSet('AIRPLANE','BOOKMARK','BUS','CAR','CLOCK','CONFIRMATION_NUMBER_ICON','DOLLAR','DESCRIPTION','EMAIL','EVENT_PERFORMER','EVENT_SEAT','FLIGHT_ARRIVAL','FLIGHT_DEPARTURE','HOTEL','HOTEL_ROOM_TYPE','INVITE','MAP_PIN','MEMBERSHIP','MULTIPLE_PEOPLE','OFFER','PERSON','PHONE','RESTAURANT_ICON','SHOPPING_CART','STAR','STORE','TICKET','TRAIN','VIDEO_CAMERA','VIDEO_PLAY')] [String] $Icon, [parameter(Mandatory = $true,ParameterSetName = "IconUrl")] [String] $IconUrl, [parameter(Mandatory = $false)] [ValidateScript( { $allowedTypes = "PSGSuite.Chat.Message.Card.OnClick" if ([string]$($_.PSTypeNames) -match "($(($allowedTypes|ForEach-Object{[RegEx]::Escape($_)}) -join '|'))") { $true } else { throw "This parameter only accepts the following types: $($allowedTypes -join ", "). The current types of the value are: $($_.PSTypeNames -join ", ")." } })] [Object] $OnClick, [parameter(Mandatory = $false,ValueFromPipeline = $true)] [Alias('InputObject')] [ValidateScript({ $allowedTypes = "PSGSuite.Chat.Message.Card.Section","PSGSuite.Chat.Message.Card","PSGSuite.Chat.Message.Card.CardAction","PSGSuite.Chat.Message.Card.Section.TextParagraph","PSGSuite.Chat.Message.Card.Section.Button","PSGSuite.Chat.Message.Card.Section.Image","PSGSuite.Chat.Message.Card.Section.KeyValue" foreach ($item in $_) { if ([string]$($item.PSTypeNames) -match "($(($allowedTypes|ForEach-Object{[RegEx]::Escape($_)}) -join '|'))") { $true } else { throw "This parameter only accepts the following types: $($allowedTypes -join ", "). The current types of the value are: $($item.PSTypeNames -join ", ")." } } })] [Object[]] $MessageSegment ) Begin { $widgetObject = @{ Webhook = @{ buttons = @() } SDK = (New-Object 'Google.Apis.HangoutsChat.v1.Data.WidgetMarkup' -Property @{ Buttons = (New-Object 'System.Collections.Generic.List[Google.Apis.HangoutsChat.v1.Data.Button]') }) } $widgetStack = @() switch ($PSCmdlet.ParameterSetName) { Text { $widgetObject['Webhook']['buttons'] += @{ textButton = @{ text = $Text onClick = $OnClick['Webhook'] } } $widgetObject['SDK'].Buttons.Add((New-Object 'Google.Apis.HangoutsChat.v1.Data.Button' -Property @{ TextButton = (New-Object 'Google.Apis.HangoutsChat.v1.Data.TextButton' -Property @{ Text = $Text OnClick = $OnClick['SDK'] }) })) | Out-Null } Icon { $widgetObject['Webhook']['buttons'] += @{ imageButton = @{ icon = $Icon onClick = $OnClick['Webhook'] } } $widgetObject['SDK'].Buttons.Add((New-Object 'Google.Apis.HangoutsChat.v1.Data.Button' -Property @{ ImageButton = (New-Object 'Google.Apis.HangoutsChat.v1.Data.ImageButton' -Property @{ Icon = $Icon OnClick = $OnClick['SDK'] }) })) | Out-Null } IconUrl { $widgetObject['Webhook']['buttons'] += @{ imageButton = @{ iconUrl = $IconUrl onClick = $OnClick['Webhook'] } } $widgetObject['SDK'].Buttons.Add((New-Object 'Google.Apis.HangoutsChat.v1.Data.Button' -Property @{ ImageButton = (New-Object 'Google.Apis.HangoutsChat.v1.Data.ImageButton' -Property @{ IconUrl = $IconUrl OnClick = $OnClick['SDK'] }) })) | Out-Null } } } Process { if ($MessageSegment) { foreach ($segment in $MessageSegment) { if ($segment.PSTypeNames[0] -in @("PSGSuite.Chat.Message.Card.Section.TextParagraph","PSGSuite.Chat.Message.Card.Section.Button","PSGSuite.Chat.Message.Card.Section.Image","PSGSuite.Chat.Message.Card.Section.KeyValue")) { $widgetStack += $segment } else { $segment } } } } End { [void]$widgetObject.PSObject.TypeNames.Insert(0,'PSGSuite.Chat.Message.Card.Section.Button') if($widgetStack) { $newWidgetStack = @() for ($i = 0;$i -lt $widgetStack.Count;$i++) { if ($i -eq ($widgetStack.Count -1) -and ($widgetStack[$i].PSTypeNames[0] -eq 'PSGSuite.Chat.Message.Card.Section.Button')) { $widgetStack[$i]['Webhook']['buttons'] += $widgetObject['Webhook']['buttons'][0] $widgetStack[$i]['SDK'].Buttons.Add($widgetObject['SDK'].Buttons[0]) | Out-Null $newWidgetStack += $widgetStack[$i] } elseif ($i -eq ($widgetStack.Count -1)) { $newWidgetStack += $widgetStack[$i] $newWidgetStack += $widgetObject } else { $newWidgetStack += $widgetStack[$i] } } $newWidgetStack } else { $widgetObject } } } Export-ModuleMember -Function 'Add-GSChatButton' function Add-GSChatCard { <# .SYNOPSIS Creates a Chat Message Card .DESCRIPTION Creates a Chat Message Card .PARAMETER HeaderTitle The header title of the card .PARAMETER HeaderSubtitle The header subtitle of the card .PARAMETER HeaderImageStyle The header image style of the card Available values are: * IMAGE * AVATAR .PARAMETER HeaderImageUrl The header image URL of the card .PARAMETER CardActions The cardActions of the card. You must use the function `New-GSChatCardAction` to create cardActions, otherwise this will throw a terminating error. .PARAMETER MessageSegment Any Chat message segment objects created with functions named `Add-GSChat*` passed through the pipeline or added directly to this parameter as values. If section widgets are passed directly to this function, a new section without a SectionHeader will be created and the widgets will be added to it .EXAMPLE Send-GSChatMessage -Text "Post job report:" -Cards $cards -Webhook (Get-GSChatWebhook JobReports) Sends a simple Chat message using the JobReports webhook .EXAMPLE Add-GSChatTextParagraph -Text "Guys...","We <b>NEED</b> to <i>stop</i> spending money on <b>crap</b>!" | Add-GSChatKeyValue -TopLabel "Chocolate Budget" -Content '$5.00' -Icon DOLLAR | Add-GSChatKeyValue -TopLabel "Actual Spending" -Content '$5,000,000!' -BottomLabel "WTF" -Icon AIRPLANE | Add-GSChatImage -ImageUrl "https://media.tenor.com/images/f78545a9b520ecf953578b4be220f26d/tenor.gif" -LinkImage | Add-GSChatCardSection -SectionHeader "Dollar bills, y'all" -OutVariable sect1 | Add-GSChatButton -Text "Launch nuke" -OnClick (Add-GSChatOnClick -Url "https://github.com/scrthq/PSGSuite") -Verbose -OutVariable button1 | Add-GSChatButton -Text "Unleash hounds" -OnClick (Add-GSChatOnClick -Url "https://admin.google.com/?hl=en&authuser=0") -Verbose -OutVariable button2 | Add-GSChatCardSection -SectionHeader "What should we do?" -OutVariable sect2 | Add-GSChatCard -HeaderTitle "Makin' moves with" -HeaderSubtitle "DEM GOODIES" -OutVariable card | Add-GSChatTextParagraph -Text "This message sent by <b>PSGSuite</b> via WebHook!" | Add-GSChatCardSection -SectionHeader "Additional Info" -OutVariable sect2 | Send-GSChatMessage -Text "Got that report, boss:" -FallbackText "Mistakes have been made..." -Webhook ReportRoom This example shows the pipeline capabilities of the Chat functions in PSGSuite. Starting from top to bottom: 1. Add a TextParagraph widget 2. Add a KeyValue with an icon 3. Add another KeyValue with a different icon 4. Add an image and create an OnClick event to open the image's URL by using the -LinkImage parameter 5. Add a new section to encapsulate the widgets sent through the pipeline before it 6. Add a TextButton that opens the PSGSuite GitHub repo when clicked 7. Add another TextButton that opens Google Admin Console when clicked 8. Wrap the 2 buttons in a new Section to divide the content 9. Wrap all widgets and sections in the pipeline so far in a Card 10. Add a new TextParagraph as a footer to the message 11. Wrap that TextParagraph in a new section 12. Send the message and include FallbackText that's displayed in the mobile notification. Since the final TextParagraph and Section are not followed by a new Card addition, Send-GSChatMessage will create a new Card just for the remaining segments then send the completed message via Webhook. The Webhook short-name is used to reference the full URL stored in the encrypted Config so it's not displayed in the actual script. .EXAMPLE Get-Service | Select-Object -First 5 | ForEach-Object { Add-GSChatKeyValue -TopLabel $_.DisplayName -Content $_.Status -BottomLabel $_.Name -Icon TICKET } | Add-GSChatCardSection -SectionHeader "Top 5 Services" | Send-GSChatMessage -Text "Service Report:" -FallbackText "Service Report" -Webhook Reports This gets the first 5 Services returned by Get-Service, creates KeyValue widgets for each, wraps it in a section with a header, then sends it to the Reports Webhook #> Param ( [parameter(Mandatory = $false,Position = 0)] [String] $HeaderTitle, [parameter(Mandatory = $false)] [String] $HeaderSubtitle, [parameter(Mandatory = $false)] [ValidateSet('IMAGE','AVATAR')] [String] $HeaderImageStyle, [parameter(Mandatory = $false)] [String] $HeaderImageUrl, [parameter(Mandatory = $false)] [ValidateScript({ $allowedTypes = "PSGSuite.Chat.Message.Card.CardAction" foreach ($item in $_) { if ([string]$($item.PSTypeNames) -match "($(($allowedTypes|ForEach-Object{[RegEx]::Escape($_)}) -join '|'))") { $true } else { throw "This parameter only accepts the following types: $($allowedTypes -join ", "). The current types of the value are: $($item.PSTypeNames -join ", ")." } } })] [Object[]] $CardActions, [parameter(Mandatory = $false,ValueFromPipeline = $true)] [Alias('InputObject')] [ValidateScript( { $allowedTypes = "PSGSuite.Chat.Message.Card.Section","PSGSuite.Chat.Message.Card","PSGSuite.Chat.Message.Card.CardAction","PSGSuite.Chat.Message.Card.Section.TextParagraph","PSGSuite.Chat.Message.Card.Section.Button","PSGSuite.Chat.Message.Card.Section.Image","PSGSuite.Chat.Message.Card.Section.KeyValue" foreach ($item in $_) { if ([string]$($item.PSTypeNames) -match "($(($allowedTypes|ForEach-Object{[RegEx]::Escape($_)}) -join '|'))") { $true } else { throw "This parameter only accepts the following types: $($allowedTypes -join ", "). The current types of the value are: $($item.PSTypeNames -join ", ")." } } })] [Object[]] $MessageSegment ) Begin { $cardObject = @{ Webhook = @{} SDK = (New-Object 'Google.Apis.HangoutsChat.v1.Data.Card') } $addlSectionWidgets = @() foreach ($key in $PSBoundParameters.Keys) { switch ($key) { HeaderTitle { if (!$cardObject['Webhook']['header']) { $cardObject['Webhook']['header'] = @{} } $cardObject['Webhook']['header']['title'] = $PSBoundParameters[$key] if (!$cardObject['SDK'].Header) { $cardObject['SDK'].Header = New-Object 'Google.Apis.HangoutsChat.v1.Data.CardHeader' } $cardObject['SDK'].Header.Title = $PSBoundParameters[$key] } HeaderSubtitle { if (!$cardObject['Webhook']['header']) { $cardObject['Webhook']['header'] = @{} } $cardObject['Webhook']['header']['subtitle'] = $PSBoundParameters[$key] if (!$cardObject['SDK'].Header) { $cardObject['SDK'].Header = New-Object 'Google.Apis.HangoutsChat.v1.Data.CardHeader' } $cardObject['SDK'].Header.Subtitle = $PSBoundParameters[$key] } HeaderImageStyle { if (!$cardObject['Webhook']['header']) { $cardObject['Webhook']['header'] = @{} } $cardObject['Webhook']['header']['imageStyle'] = $PSBoundParameters[$key] if (!$cardObject['SDK'].Header) { $cardObject['SDK'].Header = New-Object 'Google.Apis.HangoutsChat.v1.Data.CardHeader' } $cardObject['SDK'].Header.ImageStyle = $PSBoundParameters[$key] } HeaderImageUrl { if (!$cardObject['Webhook']['header']) { $cardObject['Webhook']['header'] = @{} } $cardObject['Webhook']['header']['imageUrl'] = $PSBoundParameters[$key] if (!$cardObject['SDK'].Header) { $cardObject['SDK'].Header = New-Object 'Google.Apis.HangoutsChat.v1.Data.CardHeader' } $cardObject['SDK'].Header.ImageUrl = $PSBoundParameters[$key] } CardActions { if (!$cardObject['Webhook']['cardActions']) { $cardObject['Webhook']['cardActions'] = @() } foreach ($cardAction in $CardActions) { $cardObject['Webhook']['cardActions'] += $cardAction['Webhook'] } if (!$cardObject['SDK'].CardActions) { $cardObject['SDK'].CardActions = New-Object 'System.Collections.Generic.List[Google.Apis.HangoutsChat.v1.Data.CardAction]' } foreach ($cardAction in $CardActions) { $cardObject['SDK'].CardActions.Add($cardAction['SDK']) | Out-Null } } } } } Process { foreach ($segment in $MessageSegment) { if ($segment.PSTypeNames[0] -eq 'PSGSuite.Chat.Message.Card') { $segment } elseif ($segment.PSTypeNames[0] -in @("PSGSuite.Chat.Message.Card.Section.TextParagraph","PSGSuite.Chat.Message.Card.Section.Button","PSGSuite.Chat.Message.Card.Section.Image","PSGSuite.Chat.Message.Card.Section.KeyValue")) { $addlSectionWidgets += $segment } elseif ($segment.PSTypeNames[0] -eq 'PSGSuite.Chat.Message.Card.CardAction') { if (!$cardObject['Webhook']['cardActions']) { $cardObject['Webhook']['cardActions'] = @() } $cardObject['Webhook']['cardActions'] += $segment['Webhook'] if (!$cardObject['SDK'].CardActions) { $cardObject['SDK'].CardActions = New-Object 'System.Collections.Generic.List[Google.Apis.HangoutsChat.v1.Data.CardAction]' } $cardObject['SDK'].CardActions.Add($segment['SDK']) | Out-Null } elseif ($segment.PSTypeNames[0] -eq 'PSGSuite.Chat.Message.Card.Section') { if (!$cardObject['Webhook']['sections']) { $cardObject['Webhook']['sections'] = @() } $cardObject['Webhook']['sections'] += $segment['Webhook'] if (!$cardObject['SDK'].Sections) { $cardObject['SDK'].Sections = New-Object 'System.Collections.Generic.List[Google.Apis.HangoutsChat.v1.Data.Section]' } $cardObject['SDK'].Sections.Add($segment['SDK']) | Out-Null } } } End { if($addlSectionWidgets) { $newWidgetStack = @() for ($i = 0;$i -lt $addlSectionWidgets.Count;$i++) { if ($newWidgetStack -and ($addlSectionWidgets[$i].PSTypeNames[0] -eq 'PSGSuite.Chat.Message.Card.Section.Button') -and ($newWidgetStack[-1].PSTypeNames[0] -eq 'PSGSuite.Chat.Message.Card.Section.Button')) { $newWidgetStack[-1]['Webhook']['buttons'] += $addlSectionWidgets[$i]['Webhook']['buttons'][0] $newWidgetStack[-1]['SDK'].Buttons.Add($addlSectionWidgets[$i]['SDK'].Buttons[0]) | Out-Null } else { $newWidgetStack += $addlSectionWidgets[$i] } } $addlSection = $newWidgetStack | Add-GSChatCardSection if (!$cardObject['Webhook']['sections']) { $cardObject['Webhook']['sections'] = @() } $cardObject['Webhook']['sections'] += $addlSection['Webhook'] if (!$cardObject['SDK'].Sections) { $cardObject['SDK'].Sections = New-Object 'System.Collections.Generic.List[Google.Apis.HangoutsChat.v1.Data.Section]' } $cardObject['SDK'].Sections.Add($addlSection['SDK']) | Out-Null } [void]$cardObject.PSObject.TypeNames.Insert(0,'PSGSuite.Chat.Message.Card') return $cardObject } } Export-ModuleMember -Function 'Add-GSChatCard' function Add-GSChatCardAction { <# .SYNOPSIS Creates a Chat CardAction .DESCRIPTION Creates a Chat CardAction .PARAMETER ActionLabel The label used to be displayed in the action menu item. .PARAMETER OnClick The OnClick event that triggers when a user clicks the CardAction You must use the function `Add-GSChatOnClick` to create OnClicks, otherwise this will throw a terminating error. .PARAMETER MessageSegment Any Chat message segment objects created with functions named `Add-GSChat*` passed through the pipeline or added directly to this parameter as values. .EXAMPLE Send-GSChatMessage -Text "Post job report:" -Cards $cards -Webhook (Get-GSChatWebhook JobReports) Sends a simple Chat message using the JobReports webhook .EXAMPLE Add-GSChatTextParagraph -Text "Guys...","We <b>NEED</b> to <i>stop</i> spending money on <b>crap</b>!" | Add-GSChatKeyValue -TopLabel "Chocolate Budget" -Content '$5.00' -Icon DOLLAR | Add-GSChatKeyValue -TopLabel "Actual Spending" -Content '$5,000,000!' -BottomLabel "WTF" -Icon AIRPLANE | Add-GSChatImage -ImageUrl "https://media.tenor.com/images/f78545a9b520ecf953578b4be220f26d/tenor.gif" -LinkImage | Add-GSChatCardSection -SectionHeader "Dollar bills, y'all" -OutVariable sect1 | Add-GSChatButton -Text "Launch nuke" -OnClick (Add-GSChatOnClick -Url "https://github.com/scrthq/PSGSuite") -Verbose -OutVariable button1 | Add-GSChatButton -Text "Unleash hounds" -OnClick (Add-GSChatOnClick -Url "https://admin.google.com/?hl=en&authuser=0") -Verbose -OutVariable button2 | Add-GSChatCardSection -SectionHeader "What should we do?" -OutVariable sect2 | Add-GSChatCard -HeaderTitle "Makin' moves with" -HeaderSubtitle "DEM GOODIES" -OutVariable card | Add-GSChatTextParagraph -Text "This message sent by <b>PSGSuite</b> via WebHook!" | Add-GSChatCardSection -SectionHeader "Additional Info" -OutVariable sect2 | Send-GSChatMessage -Text "Got that report, boss:" -FallbackText "Mistakes have been made..." -Webhook ReportRoom This example shows the pipeline capabilities of the Chat functions in PSGSuite. Starting from top to bottom: 1. Add a TextParagraph widget 2. Add a KeyValue with an icon 3. Add another KeyValue with a different icon 4. Add an image and create an OnClick event to open the image's URL by using the -LinkImage parameter 5. Add a new section to encapsulate the widgets sent through the pipeline before it 6. Add a TextButton that opens the PSGSuite GitHub repo when clicked 7. Add another TextButton that opens Google Admin Console when clicked 8. Wrap the 2 buttons in a new Section to divide the content 9. Wrap all widgets and sections in the pipeline so far in a Card 10. Add a new TextParagraph as a footer to the message 11. Wrap that TextParagraph in a new section 12. Send the message and include FallbackText that's displayed in the mobile notification. Since the final TextParagraph and Section are not followed by a new Card addition, Send-GSChatMessage will create a new Card just for the remaining segments then send the completed message via Webhook. The Webhook short-name is used to reference the full URL stored in the encrypted Config so it's not displayed in the actual script. .EXAMPLE Get-Service | Select-Object -First 5 | ForEach-Object { Add-GSChatKeyValue -TopLabel $_.DisplayName -Content $_.Status -BottomLabel $_.Name -Icon TICKET } | Add-GSChatCardSection -SectionHeader "Top 5 Services" | Send-GSChatMessage -Text "Service Report:" -FallbackText "Service Report" -Webhook Reports This gets the first 5 Services returned by Get-Service, creates KeyValue widgets for each, wraps it in a section with a header, then sends it to the Reports Webhook #> Param ( [parameter(Mandatory = $true,Position = 0)] [String] $ActionLabel, [parameter(Mandatory = $true,Position = 1)] [ValidateScript({ $allowedTypes = "PSGSuite.Chat.Message.Card.OnClick" if ([string]$($_.PSTypeNames) -match "($(($allowedTypes|ForEach-Object{[RegEx]::Escape($_)}) -join '|'))") { $true } else { throw "This parameter only accepts the following types: $($allowedTypes -join ", "). The current types of the value are: $($_.PSTypeNames -join ", ")." } })] [Object] $OnClick, [parameter(Mandatory = $false,ValueFromPipeline = $true)] [Alias('InputObject')] [ValidateScript({ $allowedTypes = "PSGSuite.Chat.Message.Card.Section","PSGSuite.Chat.Message.Card","PSGSuite.Chat.Message.Card.CardAction","PSGSuite.Chat.Message.Card.Section.TextParagraph","PSGSuite.Chat.Message.Card.Section.Button","PSGSuite.Chat.Message.Card.Section.Image","PSGSuite.Chat.Message.Card.Section.KeyValue" foreach ($item in $_) { if ([string]$($item.PSTypeNames) -match "($(($allowedTypes|ForEach-Object{[RegEx]::Escape($_)}) -join '|'))") { $true } else { throw "This parameter only accepts the following types: $($allowedTypes -join ", "). The current types of the value are: $($item.PSTypeNames -join ", ")." } } })] [Object[]] $MessageSegment ) Begin { $cardActionObject = @{ Webhook = @{ actionLabel = $ActionLabel onClick = $OnClick['Webhook'] } SDK = (New-Object 'Google.Apis.HangoutsChat.v1.Data.CardAction' -Property @{ ActionLabel = $ActionLabel OnClick = $OnClick['SDK'] }) } $widgetStack = @() } Process { foreach ($segment in $MessageSegment) { if ($segment.PSTypeNames[0] -in @("PSGSuite.Chat.Message.Card.Section.TextParagraph","PSGSuite.Chat.Message.Card.Section.Button","PSGSuite.Chat.Message.Card.Section.Image","PSGSuite.Chat.Message.Card.Section.KeyValue")) { $widgetStack += $segment } else { $segment } } } End { [void]$cardActionObject.PSObject.TypeNames.Insert(0,'PSGSuite.Chat.Message.Card.CardAction') if($widgetStack) { $widgetStack += $cardActionObject $widgetStack } else { $cardActionObject } } } Export-ModuleMember -Function 'Add-GSChatCardAction' function Add-GSChatCardSection { <# .SYNOPSIS Creates a Chat Message Card Section .DESCRIPTION Creates a Chat Message Card Section .PARAMETER SectionHeader The header title of the section .PARAMETER MessageSegment Any Chat message segment objects created with functions named `Add-GSChat*` passed through the pipeline or added directly to this parameter as values. .EXAMPLE Send-GSChatMessage -Text "Post job report:" -Cards $cards -Webhook (Get-GSChatWebhook JobReports) Sends a simple Chat message using the JobReports webhook .EXAMPLE Add-GSChatTextParagraph -Text "Guys...","We <b>NEED</b> to <i>stop</i> spending money on <b>crap</b>!" | Add-GSChatKeyValue -TopLabel "Chocolate Budget" -Content '$5.00' -Icon DOLLAR | Add-GSChatKeyValue -TopLabel "Actual Spending" -Content '$5,000,000!' -BottomLabel "WTF" -Icon AIRPLANE | Add-GSChatImage -ImageUrl "https://media.tenor.com/images/f78545a9b520ecf953578b4be220f26d/tenor.gif" -LinkImage | Add-GSChatCardSection -SectionHeader "Dollar bills, y'all" -OutVariable sect1 | Add-GSChatButton -Text "Launch nuke" -OnClick (Add-GSChatOnClick -Url "https://github.com/scrthq/PSGSuite") -Verbose -OutVariable button1 | Add-GSChatButton -Text "Unleash hounds" -OnClick (Add-GSChatOnClick -Url "https://admin.google.com/?hl=en&authuser=0") -Verbose -OutVariable button2 | Add-GSChatCardSection -SectionHeader "What should we do?" -OutVariable sect2 | Add-GSChatCard -HeaderTitle "Makin' moves with" -HeaderSubtitle "DEM GOODIES" -OutVariable card | Add-GSChatTextParagraph -Text "This message sent by <b>PSGSuite</b> via WebHook!" | Add-GSChatCardSection -SectionHeader "Additional Info" -OutVariable sect2 | Send-GSChatMessage -Text "Got that report, boss:" -FallbackText "Mistakes have been made..." -Webhook ReportRoom This example shows the pipeline capabilities of the Chat functions in PSGSuite. Starting from top to bottom: 1. Add a TextParagraph widget 2. Add a KeyValue with an icon 3. Add another KeyValue with a different icon 4. Add an image and create an OnClick event to open the image's URL by using the -LinkImage parameter 5. Add a new section to encapsulate the widgets sent through the pipeline before it 6. Add a TextButton that opens the PSGSuite GitHub repo when clicked 7. Add another TextButton that opens Google Admin Console when clicked 8. Wrap the 2 buttons in a new Section to divide the content 9. Wrap all widgets and sections in the pipeline so far in a Card 10. Add a new TextParagraph as a footer to the message 11. Wrap that TextParagraph in a new section 12. Send the message and include FallbackText that's displayed in the mobile notification. Since the final TextParagraph and Section are not followed by a new Card addition, Send-GSChatMessage will create a new Card just for the remaining segments then send the completed message via Webhook. The Webhook short-name is used to reference the full URL stored in the encrypted Config so it's not displayed in the actual script. .EXAMPLE Get-Service | Select-Object -First 5 | ForEach-Object { Add-GSChatKeyValue -TopLabel $_.DisplayName -Content $_.Status -BottomLabel $_.Name -Icon TICKET } | Add-GSChatCardSection -SectionHeader "Top 5 Services" | Send-GSChatMessage -Text "Service Report:" -FallbackText "Service Report" -Webhook Reports This gets the first 5 Services returned by Get-Service, creates KeyValue widgets for each, wraps it in a section with a header, then sends it to the Reports Webhook #> Param ( [parameter(Mandatory = $false,Position = 0)] [String] $SectionHeader, [parameter(Mandatory = $true,ValueFromPipeline = $true)] [Alias('InputObject')] [ValidateScript({ $allowedTypes = "PSGSuite.Chat.Message.Card.Section","PSGSuite.Chat.Message.Card","PSGSuite.Chat.Message.Card.CardAction","PSGSuite.Chat.Message.Card.Section.TextParagraph","PSGSuite.Chat.Message.Card.Section.Button","PSGSuite.Chat.Message.Card.Section.Image","PSGSuite.Chat.Message.Card.Section.KeyValue" foreach ($item in $_) { if ([string]$($item.PSTypeNames) -match "($(($allowedTypes|ForEach-Object{[RegEx]::Escape($_)}) -join '|'))") { $true } else { throw "This parameter only accepts the following types: $($allowedTypes -join ", "). The current types of the value are: $($item.PSTypeNames -join ", ")." } } })] [Object[]] $MessageSegment ) Begin { $sectionObject = @{ Webhook = @{ widgets = @() } SDK = (New-Object 'Google.Apis.HangoutsChat.v1.Data.Section' -Property @{ Widgets = (New-Object 'System.Collections.Generic.List[Google.Apis.HangoutsChat.v1.Data.WidgetMarkup]') }) } if ($PSBoundParameters.Keys -contains 'SectionHeader') { $sectionObject['Webhook']['header'] = $SectionHeader $sectionObject['SDK'].Header = $SectionHeader } } Process { foreach ($segment in $MessageSegment) { if ($segment.PSTypeNames[0] -in @("PSGSuite.Chat.Message.Card.Section.TextParagraph","PSGSuite.Chat.Message.Card.Section.Button","PSGSuite.Chat.Message.Card.Section.Image","PSGSuite.Chat.Message.Card.Section.KeyValue")) { $sectionObject['Webhook']['widgets'] += $segment['Webhook'] $sectionObject['SDK'].Widgets.Add($segment['SDK']) } else { $segment } } } End { [void]$sectionObject.PSObject.TypeNames.Insert(0,'PSGSuite.Chat.Message.Card.Section') return $sectionObject } } Export-ModuleMember -Function 'Add-GSChatCardSection' function Add-GSChatImage { <# .SYNOPSIS Creates a Chat Image widget to include in a section .DESCRIPTION Creates a Chat Image widget to include in a section .PARAMETER ImageUrl The Url of the Image .PARAMETER AspectRatio The AspectRatio of the Image .PARAMETER LinkImage If $true, automatically creates the OnClick event for the image to open the image URL .PARAMETER OnClick The OnClick event that triggers when a user clicks the KeyValue You must use the function `Add-GSChatOnClick` to create OnClicks, otherwise this will throw a terminating error. .PARAMETER MessageSegment Any Chat message segment objects created with functions named `Add-GSChat*` passed through the pipeline or added directly to this parameter as values. .EXAMPLE Send-GSChatMessage -Text "Post job report:" -Cards $cards -Webhook (Get-GSChatWebhook JobReports) Sends a simple Chat message using the JobReports webhook .EXAMPLE Add-GSChatTextParagraph -Text "Guys...","We <b>NEED</b> to <i>stop</i> spending money on <b>crap</b>!" | Add-GSChatKeyValue -TopLabel "Chocolate Budget" -Content '$5.00' -Icon DOLLAR | Add-GSChatKeyValue -TopLabel "Actual Spending" -Content '$5,000,000!' -BottomLabel "WTF" -Icon AIRPLANE | Add-GSChatImage -ImageUrl "https://media.tenor.com/images/f78545a9b520ecf953578b4be220f26d/tenor.gif" -LinkImage | Add-GSChatCardSection -SectionHeader "Dollar bills, y'all" -OutVariable sect1 | Add-GSChatButton -Text "Launch nuke" -OnClick (Add-GSChatOnClick -Url "https://github.com/scrthq/PSGSuite") -Verbose -OutVariable button1 | Add-GSChatButton -Text "Unleash hounds" -OnClick (Add-GSChatOnClick -Url "https://admin.google.com/?hl=en&authuser=0") -Verbose -OutVariable button2 | Add-GSChatCardSection -SectionHeader "What should we do?" -OutVariable sect2 | Add-GSChatCard -HeaderTitle "Makin' moves with" -HeaderSubtitle "DEM GOODIES" -OutVariable card | Add-GSChatTextParagraph -Text "This message sent by <b>PSGSuite</b> via WebHook!" | Add-GSChatCardSection -SectionHeader "Additional Info" -OutVariable sect2 | Send-GSChatMessage -Text "Got that report, boss:" -FallbackText "Mistakes have been made..." -Webhook ReportRoom This example shows the pipeline capabilities of the Chat functions in PSGSuite. Starting from top to bottom: 1. Add a TextParagraph widget 2. Add a KeyValue with an icon 3. Add another KeyValue with a different icon 4. Add an image and create an OnClick event to open the image's URL by using the -LinkImage parameter 5. Add a new section to encapsulate the widgets sent through the pipeline before it 6. Add a TextButton that opens the PSGSuite GitHub repo when clicked 7. Add another TextButton that opens Google Admin Console when clicked 8. Wrap the 2 buttons in a new Section to divide the content 9. Wrap all widgets and sections in the pipeline so far in a Card 10. Add a new TextParagraph as a footer to the message 11. Wrap that TextParagraph in a new section 12. Send the message and include FallbackText that's displayed in the mobile notification. Since the final TextParagraph and Section are not followed by a new Card addition, Send-GSChatMessage will create a new Card just for the remaining segments then send the completed message via Webhook. The Webhook short-name is used to reference the full URL stored in the encrypted Config so it's not displayed in the actual script. .EXAMPLE Get-Service | Select-Object -First 5 | ForEach-Object { Add-GSChatKeyValue -TopLabel $_.DisplayName -Content $_.Status -BottomLabel $_.Name -Icon TICKET } | Add-GSChatCardSection -SectionHeader "Top 5 Services" | Send-GSChatMessage -Text "Service Report:" -FallbackText "Service Report" -Webhook Reports This gets the first 5 Services returned by Get-Service, creates KeyValue widgets for each, wraps it in a section with a header, then sends it to the Reports Webhook #> [CmdletBinding(DefaultParameterSetName = "LinkImage")] Param ( [parameter(Mandatory = $true)] [String] $ImageUrl, [parameter(Mandatory = $false)] [Double] $AspectRatio, [parameter(Mandatory = $false,ParameterSetName = "LinkImage")] [Switch] $LinkImage, [parameter(Mandatory = $false,ParameterSetName = "OnClick")] [ValidateScript( { $allowedTypes = "PSGSuite.Chat.Message.Card.OnClick" if ([string]$($_.PSTypeNames) -match "($(($allowedTypes|ForEach-Object{[RegEx]::Escape($_)}) -join '|'))") { $true } else { throw "This parameter only accepts the following types: $($allowedTypes -join ", "). The current types of the value are: $($_.PSTypeNames -join ", ")." } })] [Object] $OnClick, [parameter(Mandatory = $false,ValueFromPipeline = $true)] [Alias('InputObject')] [ValidateScript({ $allowedTypes = "PSGSuite.Chat.Message.Card.Section","PSGSuite.Chat.Message.Card","PSGSuite.Chat.Message.Card.CardAction","PSGSuite.Chat.Message.Card.Section.TextParagraph","PSGSuite.Chat.Message.Card.Section.Button","PSGSuite.Chat.Message.Card.Section.Image","PSGSuite.Chat.Message.Card.Section.KeyValue" foreach ($item in $_) { if ([string]$($item.PSTypeNames) -match "($(($allowedTypes|ForEach-Object{[RegEx]::Escape($_)}) -join '|'))") { $true } else { throw "This parameter only accepts the following types: $($allowedTypes -join ", "). The current types of the value are: $($item.PSTypeNames -join ", ")." } } })] [Object[]] $MessageSegment ) Begin { $widgetObject = @{ Webhook = @{ image = @{} } SDK = (New-Object 'Google.Apis.HangoutsChat.v1.Data.WidgetMarkup' -Property @{ Image = (New-Object 'Google.Apis.HangoutsChat.v1.Data.Image') }) } $widgetStack = @() foreach ($key in $PSBoundParameters.Keys) { switch ($key) { ImageUrl { $widgetObject['Webhook']['image']['imageUrl'] = $PSBoundParameters[$key] $widgetObject['SDK'].Image.ImageUrl = $PSBoundParameters[$key] } AspectRatio { $widgetObject['Webhook']['image']['aspectRatio'] = $PSBoundParameters[$key] $widgetObject['SDK'].Image.AspectRatio = $PSBoundParameters[$key] } OnClick { $widgetObject['Webhook']['image']['onClick'] = $PSBoundParameters[$key]['Webhook'] $widgetObject['SDK'].Image.OnClick = $PSBoundParameters[$key]['SDK'] } } } if ($LinkImage) { $newOnClick = Add-GSChatOnClick -Url $ImageUrl $widgetObject['Webhook']['image']['onClick'] = $newOnClick['Webhook'] $widgetObject['SDK'].Image.OnClick = $newOnClick['SDK'] } } Process { foreach ($segment in $MessageSegment) { if ($segment.PSTypeNames[0] -in @("PSGSuite.Chat.Message.Card.Section.TextParagraph","PSGSuite.Chat.Message.Card.Section.Button","PSGSuite.Chat.Message.Card.Section.Image","PSGSuite.Chat.Message.Card.Section.KeyValue")) { $widgetStack += $segment } else { $segment } } } End { [void]$widgetObject.PSObject.TypeNames.Insert(0,'PSGSuite.Chat.Message.Card.Section.Image') if ($widgetStack) { $widgetStack += $widgetObject $widgetStack } else { $widgetObject } } } Export-ModuleMember -Function 'Add-GSChatImage' function Add-GSChatKeyValue { <# .SYNOPSIS Creates a Chat KeyValue widget to include in a section .DESCRIPTION Creates a Chat KeyValue widget to include in a section .PARAMETER TopLabel The TopLabel for the KeyValue .PARAMETER Content The Content for the KeyValue .PARAMETER BottomLabel The BottomLabel for the KeyValue .PARAMETER Icon The icon to display next to the KeyValue Available values are: * AIRPLANE * BOOKMARK * BUS * CAR * CLOCK * CONFIRMATION_NUMBER_ICON * DOLLAR * DESCRIPTION * EVENT_PERFORMER * EVENT_SEAT * FLIGHT_ARRIVAL * FLIGHT_DEPARTURE * HOTEL * HOTEL_ROOM_TYPE * INVITE * MAP_PIN * MEMBERSHIP * MULTIPLE_PEOPLE * OFFER * PERSON * PHONE * RESTAURANT_ICON * SHOPPING_CART * STAR * STORE * TICKET * TRAIN * VIDEO_CAMERA * VIDEO_PLAY .PARAMETER IconUrl The Url of the icon to display next to the KeyValue .PARAMETER ContentMultiline Whether the content of the KeyValue is multiline or not .PARAMETER OnClick The OnClick event that triggers when a user clicks the KeyValue You must use the function `Add-GSChatOnClick` to create OnClicks, otherwise this will throw a terminating error. .PARAMETER Button A button to add to the KeyValue You must use the function `Add-GSChatButton` to create Buttons, otherwise this will throw a terminating error. .PARAMETER MessageSegment Any Chat message segment objects created with functions named `Add-GSChat*` passed through the pipeline or added directly to this parameter as values. .EXAMPLE Send-GSChatMessage -Text "Post job report:" -Cards $cards -Webhook (Get-GSChatWebhook JobReports) Sends a simple Chat message using the JobReports webhook .EXAMPLE Add-GSChatTextParagraph -Text "Guys...","We <b>NEED</b> to <i>stop</i> spending money on <b>crap</b>!" | Add-GSChatKeyValue -TopLabel "Chocolate Budget" -Content '$5.00' -Icon DOLLAR | Add-GSChatKeyValue -TopLabel "Actual Spending" -Content '$5,000,000!' -BottomLabel "WTF" -Icon AIRPLANE | Add-GSChatImage -ImageUrl "https://media.tenor.com/images/f78545a9b520ecf953578b4be220f26d/tenor.gif" -LinkImage | Add-GSChatCardSection -SectionHeader "Dollar bills, y'all" -OutVariable sect1 | Add-GSChatButton -Text "Launch nuke" -OnClick (Add-GSChatOnClick -Url "https://github.com/scrthq/PSGSuite") -Verbose -OutVariable button1 | Add-GSChatButton -Text "Unleash hounds" -OnClick (Add-GSChatOnClick -Url "https://admin.google.com/?hl=en&authuser=0") -Verbose -OutVariable button2 | Add-GSChatCardSection -SectionHeader "What should we do?" -OutVariable sect2 | Add-GSChatCard -HeaderTitle "Makin' moves with" -HeaderSubtitle "DEM GOODIES" -OutVariable card | Add-GSChatTextParagraph -Text "This message sent by <b>PSGSuite</b> via WebHook!" | Add-GSChatCardSection -SectionHeader "Additional Info" -OutVariable sect2 | Send-GSChatMessage -Text "Got that report, boss:" -FallbackText "Mistakes have been made..." -Webhook ReportRoom This example shows the pipeline capabilities of the Chat functions in PSGSuite. Starting from top to bottom: 1. Add a TextParagraph widget 2. Add a KeyValue with an icon 3. Add another KeyValue with a different icon 4. Add an image and create an OnClick event to open the image's URL by using the -LinkImage parameter 5. Add a new section to encapsulate the widgets sent through the pipeline before it 6. Add a TextButton that opens the PSGSuite GitHub repo when clicked 7. Add another TextButton that opens Google Admin Console when clicked 8. Wrap the 2 buttons in a new Section to divide the content 9. Wrap all widgets and sections in the pipeline so far in a Card 10. Add a new TextParagraph as a footer to the message 11. Wrap that TextParagraph in a new section 12. Send the message and include FallbackText that's displayed in the mobile notification. Since the final TextParagraph and Section are not followed by a new Card addition, Send-GSChatMessage will create a new Card just for the remaining segments then send the completed message via Webhook. The Webhook short-name is used to reference the full URL stored in the encrypted Config so it's not displayed in the actual script. .EXAMPLE Get-Service | Select-Object -First 5 | ForEach-Object { Add-GSChatKeyValue -TopLabel $_.DisplayName -Content $_.Status -BottomLabel $_.Name -Icon TICKET } | Add-GSChatCardSection -SectionHeader "Top 5 Services" | Send-GSChatMessage -Text "Service Report:" -FallbackText "Service Report" -Webhook Reports This gets the first 5 Services returned by Get-Service, creates KeyValue widgets for each, wraps it in a section with a header, then sends it to the Reports Webhook #> [CmdletBinding(DefaultParameterSetName = "Icon")] Param ( [parameter(Mandatory = $false)] [String] $TopLabel, [parameter(Mandatory = $false)] [String] $Content, [parameter(Mandatory = $false)] [String] $BottomLabel, [parameter(Mandatory = $false,ParameterSetName = "Icon")] [ValidateSet('AIRPLANE','BOOKMARK','BUS','CAR','CLOCK','CONFIRMATION_NUMBER_ICON','DOLLAR','DESCRIPTION','EMAIL','EVENT_PERFORMER','EVENT_SEAT','FLIGHT_ARRIVAL','FLIGHT_DEPARTURE','HOTEL','HOTEL_ROOM_TYPE','INVITE','MAP_PIN','MEMBERSHIP','MULTIPLE_PEOPLE','OFFER','PERSON','PHONE','RESTAURANT_ICON','SHOPPING_CART','STAR','STORE','TICKET','TRAIN','VIDEO_CAMERA','VIDEO_PLAY')] [String] $Icon, [parameter(Mandatory = $false,ParameterSetName = "IconUrl")] [String] $IconUrl, [parameter(Mandatory = $false)] [Switch] $ContentMultiline, [parameter(Mandatory = $false)] [ValidateScript( { $allowedTypes = "PSGSuite.Chat.Message.Card.OnClick" if ([string]$($_.PSTypeNames) -match "($(($allowedTypes|ForEach-Object{[RegEx]::Escape($_)}) -join '|'))") { $true } else { throw "This parameter only accepts the following types: $($allowedTypes -join ", "). The current types of the value are: $($_.PSTypeNames -join ", ")." } })] [Object] $OnClick, [parameter(Mandatory = $false)] [ValidateScript( { $allowedTypes = "PSGSuite.Chat.Message.Card.Section.Button" if ([string]$($_.PSTypeNames) -match "($(($allowedTypes|ForEach-Object{[RegEx]::Escape($_)}) -join '|'))") { $true } else { throw "This parameter only accepts the following types: $($allowedTypes -join ", "). The current types of the value are: $($_.PSTypeNames -join ", ")." } })] [Object] $Button, [parameter(Mandatory = $false,ValueFromPipeline = $true)] [Alias('InputObject')] [ValidateScript({ $allowedTypes = "PSGSuite.Chat.Message.Card.Section","PSGSuite.Chat.Message.Card","PSGSuite.Chat.Message.Card.CardAction","PSGSuite.Chat.Message.Card.Section.TextParagraph","PSGSuite.Chat.Message.Card.Section.Button","PSGSuite.Chat.Message.Card.Section.Image","PSGSuite.Chat.Message.Card.Section.KeyValue" foreach ($item in $_) { if ([string]$($item.PSTypeNames) -match "($(($allowedTypes|ForEach-Object{[RegEx]::Escape($_)}) -join '|'))") { $true } else { throw "This parameter only accepts the following types: $($allowedTypes -join ", "). The current types of the value are: $($item.PSTypeNames -join ", ")." } } })] [Object[]] $MessageSegment ) Begin { $widgetObject = @{ Webhook = @{ keyValue = @{} } SDK = (New-Object 'Google.Apis.HangoutsChat.v1.Data.WidgetMarkup' -Property @{ KeyValue = (New-Object 'Google.Apis.HangoutsChat.v1.Data.KeyValue') }) } $widgetStack = @() foreach ($key in $PSBoundParameters.Keys) { switch ($key) { TopLabel { $widgetObject['Webhook']['keyValue']['topLabel'] = $PSBoundParameters[$key] $widgetObject['SDK'].KeyValue.TopLabel = $PSBoundParameters[$key] } Content { $widgetObject['Webhook']['keyValue']['content'] = $PSBoundParameters[$key] $widgetObject['SDK'].KeyValue.Content = $PSBoundParameters[$key] } BottomLabel { $widgetObject['Webhook']['keyValue']['bottomLabel'] = $PSBoundParameters[$key] $widgetObject['SDK'].KeyValue.BottomLabel = $PSBoundParameters[$key] } Icon { $widgetObject['Webhook']['keyValue']['icon'] = $PSBoundParameters[$key] $widgetObject['SDK'].KeyValue.Icon = $PSBoundParameters[$key] } IconUrl { $widgetObject['Webhook']['keyValue']['iconUrl'] = $PSBoundParameters[$key] $widgetObject['SDK'].KeyValue.IconUrl = $PSBoundParameters[$key] } ContentMultiline { $widgetObject['Webhook']['keyValue']['contentMultiline'] = $PSBoundParameters[$key] $widgetObject['SDK'].KeyValue.ContentMultiline = $PSBoundParameters[$key] } OnClick { $widgetObject['Webhook']['keyValue']['onClick'] = $PSBoundParameters[$key]['Webhook'] $widgetObject['SDK'].KeyValue.OnClick = $PSBoundParameters[$key]['SDK'] } Button { $widgetObject['Webhook']['keyValue']['button'] = $PSBoundParameters[$key]['Webhook'] $widgetObject['SDK'].KeyValue.Button = $PSBoundParameters[$key]['SDK'] } } } } Process { foreach ($segment in $MessageSegment) { if ($segment.PSTypeNames[0] -in @("PSGSuite.Chat.Message.Card.Section.TextParagraph","PSGSuite.Chat.Message.Card.Section.Button","PSGSuite.Chat.Message.Card.Section.Image","PSGSuite.Chat.Message.Card.Section.KeyValue")) { $widgetStack += $segment } else { $segment } } } End { [void]$widgetObject.PSObject.TypeNames.Insert(0,'PSGSuite.Chat.Message.Card.Section.KeyValue') if ($widgetStack) { $widgetStack += $widgetObject $widgetStack } else { $widgetObject } } } Export-ModuleMember -Function 'Add-GSChatKeyValue' function Add-GSChatOnClick { <# .SYNOPSIS Creates a Chat OnClick action to include in a widget .DESCRIPTION Creates a Chat OnClick action to include in a widget .PARAMETER Url The Url to open for an OpenLink action on click .PARAMETER ActionMethodName Apps Script function to invoke when the containing element is clicked/activated. .PARAMETER ActionParameters A hashtable containing key/value pairs of parameters to pass to the ActionMethod .EXAMPLE Send-GSChatMessage -Text "Post job report:" -Cards $cards -Webhook (Get-GSChatWebhook JobReports) Sends a simple Chat message using the JobReports webhook .EXAMPLE Add-GSChatTextParagraph -Text "Guys...","We <b>NEED</b> to <i>stop</i> spending money on <b>crap</b>!" | Add-GSChatKeyValue -TopLabel "Chocolate Budget" -Content '$5.00' -Icon DOLLAR | Add-GSChatKeyValue -TopLabel "Actual Spending" -Content '$5,000,000!' -BottomLabel "WTF" -Icon AIRPLANE | Add-GSChatImage -ImageUrl "https://media.tenor.com/images/f78545a9b520ecf953578b4be220f26d/tenor.gif" -LinkImage | Add-GSChatCardSection -SectionHeader "Dollar bills, y'all" -OutVariable sect1 | Add-GSChatButton -Text "Launch nuke" -OnClick (Add-GSChatOnClick -Url "https://github.com/scrthq/PSGSuite") -Verbose -OutVariable button1 | Add-GSChatButton -Text "Unleash hounds" -OnClick (Add-GSChatOnClick -Url "https://admin.google.com/?hl=en&authuser=0") -Verbose -OutVariable button2 | Add-GSChatCardSection -SectionHeader "What should we do?" -OutVariable sect2 | Add-GSChatCard -HeaderTitle "Makin' moves with" -HeaderSubtitle "DEM GOODIES" -OutVariable card | Add-GSChatTextParagraph -Text "This message sent by <b>PSGSuite</b> via WebHook!" | Add-GSChatCardSection -SectionHeader "Additional Info" -OutVariable sect2 | Send-GSChatMessage -Text "Got that report, boss:" -FallbackText "Mistakes have been made..." -Webhook ReportRoom This example shows the pipeline capabilities of the Chat functions in PSGSuite. Starting from top to bottom: 1. Add a TextParagraph widget 2. Add a KeyValue with an icon 3. Add another KeyValue with a different icon 4. Add an image and create an OnClick event to open the image's URL by using the -LinkImage parameter 5. Add a new section to encapsulate the widgets sent through the pipeline before it 6. Add a TextButton that opens the PSGSuite GitHub repo when clicked 7. Add another TextButton that opens Google Admin Console when clicked 8. Wrap the 2 buttons in a new Section to divide the content 9. Wrap all widgets and sections in the pipeline so far in a Card 10. Add a new TextParagraph as a footer to the message 11. Wrap that TextParagraph in a new section 12. Send the message and include FallbackText that's displayed in the mobile notification. Since the final TextParagraph and Section are not followed by a new Card addition, Send-GSChatMessage will create a new Card just for the remaining segments then send the completed message via Webhook. The Webhook short-name is used to reference the full URL stored in the encrypted Config so it's not displayed in the actual script. .EXAMPLE Get-Service | Select-Object -First 5 | ForEach-Object { Add-GSChatKeyValue -TopLabel $_.DisplayName -Content $_.Status -BottomLabel $_.Name -Icon TICKET } | Add-GSChatCardSection -SectionHeader "Top 5 Services" | Send-GSChatMessage -Text "Service Report:" -FallbackText "Service Report" -Webhook Reports This gets the first 5 Services returned by Get-Service, creates KeyValue widgets for each, wraps it in a section with a header, then sends it to the Reports Webhook #> [CmdletBinding(DefaultParameterSetName = "OpenLink")] Param ( [parameter(Mandatory = $true,Position = 0,ParameterSetName = "OpenLink")] [String] $Url, [parameter(Mandatory = $true,ParameterSetName = "Action")] [String] $ActionMethodName, [parameter(Mandatory = $false,ParameterSetName = "Action")] [Hashtable[]] $ActionParameters ) Begin { $onClickObject = @{ Webhook = @{} SDK = (New-Object 'Google.Apis.HangoutsChat.v1.Data.OnClick') } } Process { switch ($PSCmdlet.ParameterSetName) { OpenLink { $onClickObject['Webhook']['openLink'] = @{ url = $Url } $onClickObject['SDK'].OpenLink = (New-Object 'Google.Apis.HangoutsChat.v1.Data.OpenLink' -Property @{ Url = $Url }) } Action { $onClickObject['Webhook']['action'] = @{ actionMethodName = $ActionMethodName } $onClickObject['SDK'].Action = (New-Object 'Google.Apis.HangoutsChat.v1.Data.FormAction' -Property @{ ActionMethodName = $ActionMethodName }) if ($PSBoundParameters.Keys -contains 'ActionParameters') { $onClickObject['Webhook']['action']['parameters'] = @() $onClickObject['SDK'].Action.Parameters = New-Object 'System.Collections.Generic.List[Google.Apis.HangoutsChat.v1.Data.ActionParameter]' foreach ($dict in $ActionParameters) { if ($dict.Keys.Count -eq 2 -and $dict.Keys -contains 'key' -and $dict.Keys -contains 'value') { $onClickObject['Webhook']['action']['parameters'] += ([PSCustomObject]@{ key = "$($dict['key'])" value = "$($dict['value'])" }) $onClickObject['SDK'].Action.Parameters.Add((New-Object 'Google.Apis.HangoutsChat.v1.Data.ActionParameter' -Property @{ Key = $dict['key'] Value = $dict['value'] })) | Out-Null } else { foreach ($key in $dict.Keys) { $onClickObject['Webhook']['action']['parameters'] += ([PSCustomObject]@{ key = "$key" value = "$($dict[$key])" }) $onClickObject['SDK'].Action.Parameters.Add((New-Object 'Google.Apis.HangoutsChat.v1.Data.ActionParameter' -Property @{ Key = $key Value = $dict[$key] })) | Out-Null } } } } } } } End { [void]$onClickObject.PSObject.TypeNames.Insert(0,'PSGSuite.Chat.Message.Card.OnClick') $onClickObject } } Export-ModuleMember -Function 'Add-GSChatOnClick' function Add-GSChatTextParagraph { <# .SYNOPSIS Creates a Chat TextParagraph widget to include in a section .DESCRIPTION Creates a Chat TextParagraph widget to include in a section .PARAMETER Text The text for the textParagraph .PARAMETER MessageSegment Any Chat message segment objects created with functions named `Add-GSChat*` passed through the pipeline or added directly to this parameter as values. .EXAMPLE Send-GSChatMessage -Text "Post job report:" -Cards $cards -Webhook (Get-GSChatWebhook JobReports) Sends a simple Chat message using the JobReports webhook .EXAMPLE Add-GSChatTextParagraph -Text "Guys...","We <b>NEED</b> to <i>stop</i> spending money on <b>crap</b>!" | Add-GSChatKeyValue -TopLabel "Chocolate Budget" -Content '$5.00' -Icon DOLLAR | Add-GSChatKeyValue -TopLabel "Actual Spending" -Content '$5,000,000!' -BottomLabel "WTF" -Icon AIRPLANE | Add-GSChatImage -ImageUrl "https://media.tenor.com/images/f78545a9b520ecf953578b4be220f26d/tenor.gif" -LinkImage | Add-GSChatCardSection -SectionHeader "Dollar bills, y'all" -OutVariable sect1 | Add-GSChatButton -Text "Launch nuke" -OnClick (Add-GSChatOnClick -Url "https://github.com/scrthq/PSGSuite") -Verbose -OutVariable button1 | Add-GSChatButton -Text "Unleash hounds" -OnClick (Add-GSChatOnClick -Url "https://admin.google.com/?hl=en&authuser=0") -Verbose -OutVariable button2 | Add-GSChatCardSection -SectionHeader "What should we do?" -OutVariable sect2 | Add-GSChatCard -HeaderTitle "Makin' moves with" -HeaderSubtitle "DEM GOODIES" -OutVariable card | Add-GSChatTextParagraph -Text "This message sent by <b>PSGSuite</b> via WebHook!" | Add-GSChatCardSection -SectionHeader "Additional Info" -OutVariable sect2 | Send-GSChatMessage -Text "Got that report, boss:" -FallbackText "Mistakes have been made..." -Webhook ReportRoom This example shows the pipeline capabilities of the Chat functions in PSGSuite. Starting from top to bottom: 1. Add a TextParagraph widget 2. Add a KeyValue with an icon 3. Add another KeyValue with a different icon 4. Add an image and create an OnClick event to open the image's URL by using the -LinkImage parameter 5. Add a new section to encapsulate the widgets sent through the pipeline before it 6. Add a TextButton that opens the PSGSuite GitHub repo when clicked 7. Add another TextButton that opens Google Admin Console when clicked 8. Wrap the 2 buttons in a new Section to divide the content 9. Wrap all widgets and sections in the pipeline so far in a Card 10. Add a new TextParagraph as a footer to the message 11. Wrap that TextParagraph in a new section 12. Send the message and include FallbackText that's displayed in the mobile notification. Since the final TextParagraph and Section are not followed by a new Card addition, Send-GSChatMessage will create a new Card just for the remaining segments then send the completed message via Webhook. The Webhook short-name is used to reference the full URL stored in the encrypted Config so it's not displayed in the actual script. .EXAMPLE Get-Service | Select-Object -First 5 | ForEach-Object { Add-GSChatKeyValue -TopLabel $_.DisplayName -Content $_.Status -BottomLabel $_.Name -Icon TICKET } | Add-GSChatCardSection -SectionHeader "Top 5 Services" | Send-GSChatMessage -Text "Service Report:" -FallbackText "Service Report" -Webhook Reports This gets the first 5 Services returned by Get-Service, creates KeyValue widgets for each, wraps it in a section with a header, then sends it to the Reports Webhook #> Param ( [parameter(Mandatory = $true,Position = 0)] [String[]] $Text, [parameter(Mandatory = $false,ValueFromPipeline = $true)] [Alias('InputObject')] [ValidateScript({ $allowedTypes = "PSGSuite.Chat.Message.Card.Section","PSGSuite.Chat.Message.Card","PSGSuite.Chat.Message.Card.CardAction","PSGSuite.Chat.Message.Card.Section.TextParagraph","PSGSuite.Chat.Message.Card.Section.Button","PSGSuite.Chat.Message.Card.Section.Image","PSGSuite.Chat.Message.Card.Section.KeyValue" foreach ($item in $_) { if ([string]$($item.PSTypeNames) -match "($(($allowedTypes|ForEach-Object{[RegEx]::Escape($_)}) -join '|'))") { $true } else { throw "This parameter only accepts the following types: $($allowedTypes -join ", "). The current types of the value are: $($item.PSTypeNames -join ", ")." } } })] [Object[]] $MessageSegment ) Begin { $widgetObject = @{ Webhook = @{ textParagraph = @{ text = ($Text -join "`n") } } SDK = (New-Object 'Google.Apis.HangoutsChat.v1.Data.WidgetMarkup' -Property @{ TextParagraph = (New-Object 'Google.Apis.HangoutsChat.v1.Data.TextParagraph' -Property @{ Text = ($Text -join "`n") }) }) } $widgetStack = @() } Process { foreach ($segment in $MessageSegment) { if ($segment.PSTypeNames[0] -in @("PSGSuite.Chat.Message.Card.Section.TextParagraph","PSGSuite.Chat.Message.Card.Section.Button","PSGSuite.Chat.Message.Card.Section.Image","PSGSuite.Chat.Message.Card.Section.KeyValue")) { $widgetStack += $segment } else { $segment } } } End { [void]$widgetObject.PSObject.TypeNames.Insert(0,'PSGSuite.Chat.Message.Card.Section.TextParagraph') if ($widgetStack) { $widgetStack += $widgetObject $widgetStack } else { $widgetObject } } } Export-ModuleMember -Function 'Add-GSChatTextParagraph' function Add-GSEventAttendee { <# .SYNOPSIS Adds an event attendee to a calendar event .DESCRIPTION Adds an event attendee to a calendar event .PARAMETER Email The email address of the attendee .PARAMETER AdditionalGuests How many additional guests, if any .PARAMETER Comment Attendee comment .PARAMETER DisplayName The attendee's name, if available .PARAMETER Optional Whether this is an optional attendee .PARAMETER Organizer Whether the attendee is the organizer of the event .PARAMETER Resource Whether the attendee is a resource .PARAMETER ResponseStatus The attendee's response status. Possible values are: * "NeedsAction": The attendee has not responded to the invitation. * "Declined": The attendee has declined the invitation. * "Tentative": The attendee has tentatively accepted the invitation. * "Accepted": The attendee has accepted the invitation .PARAMETER InputObject Used for pipeline input of an existing UserAddress object to strip the extra attributes and prevent errors #> [CmdletBinding(DefaultParameterSetName = "InputObject")] Param ( [Parameter(Mandatory = $true,ParameterSetName = "Fields")] [String] $Email, [Parameter(Mandatory = $false,ParameterSetName = "Fields")] [Int] $AdditionalGuests, [Parameter(Mandatory = $false,ParameterSetName = "Fields")] [String] $Comment, [Parameter(Mandatory = $false,ParameterSetName = "Fields")] [String] $DisplayName, [Parameter(Mandatory = $false,ParameterSetName = "Fields")] [Switch] $Optional, [Parameter(Mandatory = $false,ParameterSetName = "Fields")] [Switch] $Organizer, [Parameter(Mandatory = $false,ParameterSetName = "Fields")] [Switch] $Resource, [Parameter(Mandatory = $false,ParameterSetName = "Fields")] [ValidateSet('NeedsAction','Declined','Tentative','Accepted')] [String] $ResponseStatus, [Parameter(Mandatory = $false,ValueFromPipeline = $true,ParameterSetName = "InputObject")] [Google.Apis.Calendar.v3.Data.EventAttendee[]] $InputObject ) Begin { $propsToWatch = @( 'AdditionalGuests' 'Comment' 'DisplayName' 'Email' 'Optional' 'Organizer' 'Resource' 'ResponseStatus' ) } Process { try { switch ($PSCmdlet.ParameterSetName) { Fields { Write-Verbose "Adding event attendee '$Email'" $obj = New-Object 'Google.Apis.Calendar.v3.Data.EventAttendee' foreach ($prop in $PSBoundParameters.Keys | Where-Object {$obj.PSObject.Properties.Name -contains $_}) { $obj.$prop = $PSBoundParameters[$prop] } $obj } InputObject { foreach ($iObj in $InputObject) { $obj = New-Object 'Google.Apis.Calendar.v3.Data.EventAttendee' foreach ($prop in $iObj.PSObject.Properties.Name | Where-Object {$obj.PSObject.Properties.Name -contains $_ -and $propsToWatch -contains $_}) { $obj.$prop = $iObj.$prop } $obj } } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Add-GSEventAttendee' function Add-GSUserAddress { <# .SYNOPSIS Builds a UserAddress object to use when creating or updating a User .DESCRIPTION Builds a UserAddress object to use when creating or updating a User .PARAMETER Country Country .PARAMETER CountryCode The country code. Uses the ISO 3166-1 standard: http://www.iso.org/iso/iso-3166-1_decoding_table .PARAMETER CustomType If the address type is custom, this property contains the custom value .PARAMETER ExtendedAddress For extended addresses, such as an address that includes a sub-region .PARAMETER Formatted A full and unstructured postal address. This is not synced with the structured address fields .PARAMETER Locality The town or city of the address .PARAMETER PoBox The post office box, if present .PARAMETER PostalCode The ZIP or postal code, if applicable .PARAMETER Primary If this is the user's primary address. The addresses list may contain only one primary address .PARAMETER Region The abbreviated province or state .PARAMETER SourceIsStructured Indicates if the user-supplied address was formatted. Formatted addresses are not currently supported .PARAMETER StreetAddress The street address, such as 1600 Amphitheatre Parkway. Whitespace within the string is ignored; however, newlines are significant .PARAMETER Type The address type. Acceptable values are: * "Custom" * "Home" * "Other" * "Work" .PARAMETER InputObject Used for pipeline input of an existing UserAddress object to strip the extra attributes and prevent errors .EXAMPLE $address = Add-GSUserAddress -Country USA -Locality Dallas -PostalCode 75000 Region TX -StreetAddress '123 South St' -Type Work -Primary $phone = Add-GSUserPhone -Type Work -Value "(800) 873-0923" -Primary $extId = Add-GSUserExternalId -Type Login_Id -Value jsmith2 $email = Add-GSUserEmail -Type work -Address jsmith@contoso.com New-GSUser -PrimaryEmail john.smith@domain.com -GivenName John -FamilyName Smith -Password (ConvertTo-SecureString -String 'Password123' -AsPlainText -Force) -ChangePasswordAtNextLogin -OrgUnitPath "/Users/New Hires" -IncludeInGlobalAddressList -Addresses $address -Phones $phone -ExternalIds $extId -Emails $email Creates a user named John Smith and adds their work address, work phone, login_id and alternate non gsuite work email to the user object. #> [CmdletBinding(DefaultParameterSetName = "InputObject")] Param ( [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [String] $Country, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [String] $CountryCode, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [String] $CustomType, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [String] $ExtendedAddress, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [String] $Formatted, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [Alias('Town', 'City')] [String] $Locality, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [String] $PoBox, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [String] $PostalCode, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [Switch] $Primary, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [Alias('State', 'Province')] [String] $Region, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [Switch] $SourceIsStructured, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [String] $StreetAddress, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [ValidateSet('Custom', 'Home', 'Other', 'Work')] [String] $Type, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ParameterSetName = "InputObject")] [Google.Apis.Admin.Directory.directory_v1.Data.UserAddress[]] $InputObject ) Begin { $propsToWatch = @( 'Country' 'CountryCode' 'CustomType' 'ExtendedAddress' 'Formatted' 'Locality' 'PoBox' 'PostalCode' 'Primary' 'Region' 'SourceIsStructured' 'StreetAddress' 'Type' ) } Process { try { switch ($PSCmdlet.ParameterSetName) { Fields { $obj = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.UserAddress' foreach ($prop in $PSBoundParameters.Keys | Where-Object {$obj.PSObject.Properties.Name -contains $_}) { $obj.$prop = $PSBoundParameters[$prop] } $obj } InputObject { foreach ($iObj in $InputObject) { $obj = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.UserAddress' foreach ($prop in $iObj.PSObject.Properties.Name | Where-Object {$obj.PSObject.Properties.Name -contains $_ -and $propsToWatch -contains $_}) { $obj.$prop = $iObj.$prop } $obj } } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Add-GSUserAddress' function Add-GSUserEmail { <# .SYNOPSIS Builds a Email object to use when creating or updating a User .DESCRIPTION Builds a Email object to use when creating or updating a User .PARAMETER Address The user's email address. Also serves as the email ID. This value can be the user's primary email address or an alias. .PARAMETER CustomType If the value of type is custom, this property contains the custom type. .PARAMETER Primary Indicates if this is the user's primary email. Only one entry can be marked as primary. .PARAMETER Type The type of the email account. Acceptable values are: * "custom" * "home" * "other" * "work" .PARAMETER InputObject Used for pipeline input of an existing Email object to strip the extra attributes and prevent errors .EXAMPLE $address = Add-GSUserAddress -Country USA -Locality Dallas -PostalCode 75000 Region TX -StreetAddress '123 South St' -Type Work -Primary $phone = Add-GSUserPhone -Type Work -Value "(800) 873-0923" -Primary $extId = Add-GSUserExternalId -Type Login_Id -Value jsmith2 $email = Add-GSUserEmail -Type work -Address jsmith@contoso.com New-GSUser -PrimaryEmail john.smith@domain.com -GivenName John -FamilyName Smith -Password (ConvertTo-SecureString -String 'Password123' -AsPlainText -Force) -ChangePasswordAtNextLogin -OrgUnitPath "/Users/New Hires" -IncludeInGlobalAddressList -Addresses $address -Phones $phone -ExternalIds $extId -Emails $email Creates a user named John Smith and adds their work address, work phone, login_id and alternate non gsuite work email to the user object. #> [CmdletBinding()] Param ( [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [String] $Address, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [String] $CustomType, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [Switch] $Primary, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [ValidateSet('custom', 'home', 'other', 'work')] [String] $Type, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ParameterSetName = "InputObject")] [Google.Apis.Admin.Directory.directory_v1.Data.UserEmail[]] $InputObject ) Begin { $propsToWatch = @( 'Address' 'CustomType' 'Type' ) } Process { try { switch ($PSCmdlet.ParameterSetName) { Fields { $obj = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.UserEmail' foreach ($prop in $PSBoundParameters.Keys | Where-Object {$obj.PSObject.Properties.Name -contains $_}) { $obj.$prop = $PSBoundParameters[$prop] } $obj } InputObject { foreach ($iObj in $InputObject) { $obj = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.UserEmail' foreach ($prop in $iObj.PSObject.Properties.Name | Where-Object {$obj.PSObject.Properties.Name -contains $_ -and $propsToWatch -contains $_}) { $obj.$prop = $iObj.$prop } $obj } } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Add-GSUserEmail' function Add-GSUserExternalId { <# .SYNOPSIS Builds a UserExternalId object to use when creating or updating a User .DESCRIPTION Builds a UserExternalId object to use when creating or updating a User .PARAMETER CustomType If the external ID type is custom, this property holds the custom type .PARAMETER Type The type of the ID. Acceptable values are: * "account" * "custom" * "customer" * "login_id" * "network" * "organization": For example, Employee ID. .PARAMETER Value The value of the ID .PARAMETER InputObject Used for pipeline input of an existing UserExternalId object to strip the extra attributes and prevent errors .EXAMPLE $address = Add-GSUserAddress -Country USA -Locality Dallas -PostalCode 75000 Region TX -StreetAddress '123 South St' -Type Work -Primary $phone = Add-GSUserPhone -Type Work -Value "(800) 873-0923" -Primary $extId = Add-GSUserExternalId -Type Login_Id -Value jsmith2 $email = Add-GSUserEmail -Type work -Address jsmith@contoso.com New-GSUser -PrimaryEmail john.smith@domain.com -GivenName John -FamilyName Smith -Password (ConvertTo-SecureString -String 'Password123' -AsPlainText -Force) -ChangePasswordAtNextLogin -OrgUnitPath "/Users/New Hires" -IncludeInGlobalAddressList -Addresses $address -Phones $phone -ExternalIds $extId -Emails $email Creates a user named John Smith and adds their work address, work phone, login_id and alternate non gsuite work email to the user object. #> [CmdletBinding(DefaultParameterSetName = "InputObject")] Param ( [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [String] $CustomType, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [ValidateSet('account', 'custom', 'customer', 'login_id', 'network', 'organization')] [String] $Type, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [Alias('ExternalId')] [String] $Value, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ParameterSetName = "InputObject")] [Google.Apis.Admin.Directory.directory_v1.Data.UserExternalId[]] $InputObject ) Begin { $propsToWatch = @( 'CustomType' 'Type' 'Value' ) } Process { try { switch ($PSCmdlet.ParameterSetName) { Fields { $obj = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.UserExternalId' foreach ($prop in $PSBoundParameters.Keys | Where-Object {$obj.PSObject.Properties.Name -contains $_}) { $obj.$prop = $PSBoundParameters[$prop] } $obj } InputObject { foreach ($iObj in $InputObject) { $obj = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.UserExternalId' foreach ($prop in $iObj.PSObject.Properties.Name | Where-Object {$obj.PSObject.Properties.Name -contains $_ -and $propsToWatch -contains $_}) { $obj.$prop = $iObj.$prop } $obj } } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Add-GSUserExternalId' function Add-GSUserOrganization { <# .SYNOPSIS Builds a Organization object to use when creating or updating a User .DESCRIPTION Builds a Organization object to use when creating or updating a User .PARAMETER CustomType If the external ID type is custom, this property holds the custom type .PARAMETER Type The type of the organization. If using a CustomType .PARAMETER Value The value of the ID .PARAMETER InputObject Used for pipeline input of an existing UserExternalId object to strip the extra attributes and prevent errors .EXAMPLE $address = Add-GSUserAddress -Country USA -Locality Dallas -PostalCode 75000 Region TX -StreetAddress '123 South St' -Type Work -Primary $phone = Add-GSUserPhone -Type Work -Value "(800) 873-0923" -Primary $extId = Add-GSUserExternalId -Type Login_Id -Value jsmith2 $email = Add-GSUserEmail -Type work -Address jsmith@contoso.com New-GSUser -PrimaryEmail john.smith@domain.com -GivenName John -FamilyName Smith -Password (ConvertTo-SecureString -String 'Password123' -AsPlainText -Force) -ChangePasswordAtNextLogin -OrgUnitPath "/Users/New Hires" -IncludeInGlobalAddressList -Addresses $address -Phones $phone -ExternalIds $extId -Emails $email Creates a user named John Smith and adds their work address, work phone, login_id and alternate non gsuite work email to the user object. #> [CmdletBinding(DefaultParameterSetName = "InputObject")] Param ( [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [String] $CostCenter, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [String] $CustomType, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [String] $Department, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [String] $Description, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [String] $Domain, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [Int] $FullTimeEquivalent, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [String] $Location, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [String] $Name, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [Switch] $Primary, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [String] $Symbol, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [String] $Title, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [String] $Type, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ParameterSetName = "InputObject")] [Google.Apis.Admin.Directory.directory_v1.Data.UserOrganization[]] $InputObject ) Begin { $propsToWatch = @( 'CostCenter' 'CustomType' 'Department' 'Description' 'Domain' 'FullTimeEquivalent' 'Location' 'Name' 'Primary' 'Symbol' 'Title' 'Type' ) if ($PSBoundParameters.Keys -contains 'CustomType') { $PSBoundParameters['Type'] = 'CUSTOM' } } Process { try { switch ($PSCmdlet.ParameterSetName) { Fields { $obj = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.UserOrganization' foreach ($prop in $PSBoundParameters.Keys | Where-Object {$obj.PSObject.Properties.Name -contains $_}) { $obj.$prop = $PSBoundParameters[$prop] } $obj } InputObject { foreach ($iObj in $InputObject) { $obj = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.UserOrganization' foreach ($prop in $iObj.PSObject.Properties.Name | Where-Object {$obj.PSObject.Properties.Name -contains $_ -and $propsToWatch -contains $_}) { $obj.$prop = $iObj.$prop } $obj } } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Add-GSUserOrganization' function Add-GSUserPhone { <# .SYNOPSIS Builds a UserPhone object to use when creating or updating a User .DESCRIPTION Builds a UserPhone object to use when creating or updating a User .PARAMETER CustomType If the value of type is custom, this property contains the custom type .PARAMETER Primary Indicates if this is the user's primary phone number. A user may only have one primary phone number .PARAMETER Type The type of phone number. Acceptable values are: * "assistant" * "callback" * "car" * "company_main" * "custom" * "grand_central" * "home" * "home_fax" * "isdn" * "main" * "mobile" * "other" * "other_fax" * "pager" * "radio" * "telex" * "tty_tdd" * "work" * "work_fax" * "work_mobile" * "work_pager" .PARAMETER Value A human-readable phone number. It may be in any telephone number format .PARAMETER InputObject Used for pipeline input of an existing UserPhone object to strip the extra attributes and prevent errors .EXAMPLE $address = Add-GSUserAddress -Country USA -Locality Dallas -PostalCode 75000 Region TX -StreetAddress '123 South St' -Type Work -Primary $phone = Add-GSUserPhone -Type Work -Value "(800) 873-0923" -Primary $extId = Add-GSUserExternalId -Type Login_Id -Value jsmith2 $email = Add-GSUserEmail -Type work -Address jsmith@contoso.com New-GSUser -PrimaryEmail john.smith@domain.com -GivenName John -FamilyName Smith -Password (ConvertTo-SecureString -String 'Password123' -AsPlainText -Force) -ChangePasswordAtNextLogin -OrgUnitPath "/Users/New Hires" -IncludeInGlobalAddressList -Addresses $address -Phones $phone -ExternalIds $extId -Emails $email Creates a user named John Smith and adds their work address, work phone, login_id and alternate non gsuite work email to the user object. #> [CmdletBinding()] Param ( [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [String] $CustomType, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [Switch] $Primary, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [ValidateSet('assistant', 'callback', 'car', 'company_main', 'custom', 'grand_central', 'home', 'home_fax', 'isdn', 'main', 'mobile', 'other', 'other_fax', 'pager', 'radio', 'telex', 'tty_tdd', 'work', 'work_fax', 'work_mobile', 'work_pager')] [String] $Type, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [Alias('Phone')] [String] $Value, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ParameterSetName = "InputObject")] [Google.Apis.Admin.Directory.directory_v1.Data.UserAddress[]] $InputObject ) Begin { $propsToWatch = @( 'CustomType' 'Type' 'Value' ) } Process { try { switch ($PSCmdlet.ParameterSetName) { Fields { $obj = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.UserPhone' foreach ($prop in $PSBoundParameters.Keys | Where-Object {$obj.PSObject.Properties.Name -contains $_}) { $obj.$prop = $PSBoundParameters[$prop] } $obj } InputObject { foreach ($iObj in $InputObject) { $obj = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.UserPhone' foreach ($prop in $iObj.PSObject.Properties.Name | Where-Object {$obj.PSObject.Properties.Name -contains $_ -and $propsToWatch -contains $_}) { $obj.$prop = $iObj.$prop } $obj } } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Add-GSUserPhone' function Add-GSUserRelation { <# .SYNOPSIS Builds a Relation object to use when creating or updating a User .DESCRIPTION Builds a Relation object to use when creating or updating a User .PARAMETER Type The type of relation. Acceptable values are: * "admin_assistant" * "assistant" * "brother" * "child" * "custom" * "domestic_partner" * "dotted_line_manager" * "exec_assistant" * "father" * "friend" * "manager" * "mother" * "parent" * "partner" * "referred_by" * "relative" * "sister" * "spouse" .PARAMETER Value The name of the person the user is related to. .PARAMETER CustomType If the value of `Type` is `custom`, this property contains the custom type. .PARAMETER InputObject Used for pipeline input of an existing UserExternalId object to strip the extra attributes and prevent errors .EXAMPLE $address = Add-GSUserAddress -Country USA -Locality Dallas -PostalCode 75000 Region TX -StreetAddress '123 South St' -Type Work -Primary $phone = Add-GSUserPhone -Type Work -Value "(800) 873-0923" -Primary $extId = Add-GSUserExternalId -Type Login_Id -Value jsmith2 $email = Add-GSUserEmail -Type work -Address jsmith@contoso.com New-GSUser -PrimaryEmail john.smith@domain.com -GivenName John -FamilyName Smith -Password (ConvertTo-SecureString -String 'Password123' -AsPlainText -Force) -ChangePasswordAtNextLogin -OrgUnitPath "/Users/New Hires" -IncludeInGlobalAddressList -Addresses $address -Phones $phone -ExternalIds $extId -Emails $email Creates a user named John Smith and adds their work address, work phone, login_id and alternate non gsuite work email to the user object. #> [CmdletBinding(DefaultParameterSetName = "InputObject")] Param ( [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [ValidateSet("admin_assistant","assistant","brother","child","custom","domestic_partner","dotted_line_manager","exec_assistant","father","friend","manager","mother","parent","partner","referred_by","relative","sister","spouse")] [String] $Type, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [String] $Value, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [String] $CustomType, [Parameter(Mandatory = $false, ParameterSetName = "Fields")] [String] $ETag, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ParameterSetName = "InputObject")] [Google.Apis.Admin.Directory.directory_v1.Data.UserRelation[]] $InputObject ) Begin { $propsToWatch = @( 'CustomType' 'Type', 'Value' 'ETag' ) if ($PSBoundParameters.Keys -contains 'CustomType') { $PSBoundParameters['Type'] = 'custom' } $PSBoundParameters['Type'] = $PSBoundParameters['Type'].ToString().ToLower() } Process { try { switch ($PSCmdlet.ParameterSetName) { Fields { $obj = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.UserRelation' foreach ($prop in $PSBoundParameters.Keys | Where-Object {$obj.PSObject.Properties.Name -contains $_}) { $obj.$prop = $PSBoundParameters[$prop] } $obj } InputObject { foreach ($iObj in $InputObject) { $obj = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.UserRelation' foreach ($prop in $iObj.PSObject.Properties.Name | Where-Object {$obj.PSObject.Properties.Name -contains $_ -and $propsToWatch -contains $_}) { $obj.$prop = $iObj.$prop } $obj } } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Add-GSUserRelation' function Add-GSUserSchemaField { <# .SYNOPSIS Builds a UserPhone object to use when creating or updating a Schema .DESCRIPTION Builds a UserPhone object to use when creating or updating a Schema .PARAMETER FieldName The name of the field .PARAMETER FieldType The type of the field. * Acceptable values are: * "BOOL": Boolean values. * "DATE": Dates in ISO-8601 format: http://www.w3.org/TR/NOTE-datetime * "DOUBLE": Double-precision floating-point values. * "EMAIL": Email addresses. * "INT64": 64-bit integer values. * "PHONE": Phone numbers. * "STRING": String values. .PARAMETER Indexed Switch specifying whether the field is indexed or not. Default: true .PARAMETER MultiValued A switch specifying whether this is a multi-valued field or not. Default: false .PARAMETER ReadAccessType Parameter description .PARAMETER InputObject Parameter description .EXAMPLE New-GSUserSchema -SchemaName "SDK" -Fields (Add-GSUserSchemaField -FieldName "string" -FieldType STRING -ReadAccessType ADMINS_AND_SELF),(Add-GSUserSchemaField -FieldName "date" -FieldType DATE -ReadAccessType ADMINS_AND_SELF) This command will create a schema named "SDK" with two fields, "string" and "date", readable by ADMINS_AND_SELF #> [CmdletBinding(DefaultParameterSetName = "InputObject")] Param ( [Parameter(Mandatory = $false,ParameterSetName = "Fields")] [String] $FieldName, [Parameter(Mandatory = $false,ParameterSetName = "Fields")] [ValidateSet("BOOL","DATE","DOUBLE","EMAIL","INT64","PHONE","STRING")] [String] $FieldType = "STRING", [Parameter(Mandatory = $false,ParameterSetName = "Fields")] [Switch] $Indexed, [Parameter(Mandatory = $false,ParameterSetName = "Fields")] [Switch] $MultiValued, [Parameter(Mandatory = $false,ParameterSetName = "Fields")] [ValidateSet("ADMINS_AND_SELF","ALL_DOMAIN_USERS")] [String] $ReadAccessType = "ADMINS_AND_SELF", [Parameter(Mandatory = $false,ValueFromPipeline = $true,ParameterSetName = "InputObject")] [Google.Apis.Admin.Directory.directory_v1.Data.SchemaFieldSpec[]] $InputObject ) Begin { $propsToWatch = @( 'FieldName' 'FieldType' 'Indexed' 'MultiValued' 'ReadAccessType' ) } Process { try { switch ($PSCmdlet.ParameterSetName) { Fields { $obj = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.SchemaFieldSpec' foreach ($prop in $PSBoundParameters.Keys | Where-Object {$obj.PSObject.Properties.Name -contains $_}) { $obj.$prop = $PSBoundParameters[$prop] } $obj } InputObject { foreach ($iObj in $InputObject) { $obj = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.SchemaFieldSpec' foreach ($prop in $iObj.PSObject.Properties.Name | Where-Object {$obj.PSObject.Properties.Name -contains $_ -and $propsToWatch -contains $_}) { $obj.$prop = $iObj.$prop } $obj } } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Add-GSUserSchemaField' function Block-CoreCLREncryptionWarning { <# .SYNOPSIS Blocks CoreCLR encryption warning from reappearing (specific to PSGSuite) .DESCRIPTION Blocks CoreCLR encryption warning from reappearing (specific to PSGSuite) by creating a blank txt file in the path: ~\.scrthq .EXAMPLE Block-CoreCLREncryptionWarning Creates the breadcrumb file to let PSGSuite know that you've acknowledged the CoreCLR encryption warning and it does not need to be displayed every time the module is imported #> New-Item -Path (Join-Path (Join-Path "~" ".scrthq") "BlockCoreCLREncryptionWarning.txt") -ItemType File -Force | Out-Null } Export-ModuleMember -Function 'Block-CoreCLREncryptionWarning' function Compare-ModuleVersion { [CmdletBinding()] Param ( [parameter(Mandatory = $false,Position = 0)] [String[]] $ModuleName = 'PSGSuite' ) Begin { $results = New-Object System.Collections.ArrayList } Process { foreach ($module in $ModuleName) { Write-Verbose "Comparing module versions for module '$module'" $result = [PSCustomObject][Ordered]@{ ModuleName = $module InstalledVersion = $null GalleryVersion = $null UpdateAvailable = $null } if ($InstalledVersion = ((Get-Module -Name $module -ListAvailable).Version | Sort-Object)[-1]) { $result.InstalledVersion = $InstalledVersion $uri = [Uri]"https://www.powershellgallery.com/api/v2/Packages?`$filter=Id eq '$($module)' and IsLatestVersion" if ($GalleryVersion = [System.Version]((Invoke-RestMethod -Uri $uri -Verbose:$false).properties.Version)) { $result.GalleryVersion = $GalleryVersion $result.UpdateAvailable = if ($InstalledVersion -ge $GalleryVersion) { $false } else { $true } } } else { Write-Warning "Module '$module' was not found on this machine; unable to compare module versions." } [void]$results.Add($result) } } End { return $results } } Export-ModuleMember -Function 'Compare-ModuleVersion' function Unblock-CoreCLREncryptionWarning { <# .SYNOPSIS Unblocks CoreCLR encryption warning to ensure it appears if applicable (specific to PSGSuite) .DESCRIPTION Unblocks CoreCLR encryption warning to ensure it appears if applicable (specific to PSGSuite) by removing the following file if it exists: ~\.scrthq\BlockCoreCLREncryptionWarning.txt .EXAMPLE Unblock-CoreCLREncryptionWarning Removes the breadcrumb file to let PSGSuite know that you want to receive the CoreCLR encryption warning if applicable #> if (Test-Path (Join-Path (Join-Path "~" ".scrthq") "BlockCoreCLREncryptionWarning.txt")) { Remove-Item -Path (Join-Path (Join-Path "~" ".scrthq") "BlockCoreCLREncryptionWarning.txt") -Force } } Export-ModuleMember -Function 'Unblock-CoreCLREncryptionWarning' function Get-GSUserLicense { <# .SYNOPSIS Gets the G Suite license information for a user or list of users .DESCRIPTION Gets the G Suite license information for a user or list of users .PARAMETER User The primary email or unique Id of the user to retrieve license information for .PARAMETER License The license SKU to retrieve information for. If excluded, searches all license SKUs .PARAMETER ProductID The product Id to list licenses for .PARAMETER PageSize The page size of the result set .EXAMPLE Get-GSUserLicense Gets the full list of licenses for the customer #> [cmdletbinding(DefaultParameterSetName = "List")] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true,ParameterSetName = "Get")] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String[]] $User, [parameter(Mandatory = $false)] [Alias("SkuId")] [ValidateSet("G-Suite-Enterprise","Google-Apps-Unlimited","Google-Apps-For-Business","Google-Apps-For-Postini","Google-Apps-Lite","Google-Drive-storage-20GB","Google-Drive-storage-50GB","Google-Drive-storage-200GB","Google-Drive-storage-400GB","Google-Drive-storage-1TB","Google-Drive-storage-2TB","Google-Drive-storage-4TB","Google-Drive-storage-8TB","Google-Drive-storage-16TB","Google-Vault","Google-Vault-Former-Employee","1010020020")] [string] $License, [parameter(Mandatory = $false,ParameterSetName = "List")] [ValidateSet("Google-Apps","Google-Drive-storage","Google-Vault")] [string[]] $ProductID = @("Google-Apps","Google-Drive-storage","Google-Vault"), [parameter(Mandatory = $false,ParameterSetName = "List")] [Alias("MaxResults")] [ValidateRange(1,1000)] [Int] $PageSize = "1000" ) Begin { if ($PSCmdlet.ParameterSetName -eq 'Get') { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/apps.licensing' ServiceType = 'Google.Apis.Licensing.v1.LicensingService' } $service = New-GoogleService @serviceParams $productHash = @{ '1010020020' = 'Google-Apps' 'G-Suite-Enterprise' = 'Google-Apps' 'Google-Apps-Unlimited' = 'Google-Apps' 'Google-Apps-For-Business' = 'Google-Apps' 'Google-Apps-For-Postini' = 'Google-Apps' 'Google-Apps-Lite' = 'Google-Apps' 'Google-Vault' = 'Google-Vault' 'Google-Vault-Former-Employee' = 'Google-Vault' 'Google-Drive-storage-20GB' = 'Google-Drive-storage' 'Google-Drive-storage-50GB' = 'Google-Drive-storage' 'Google-Drive-storage-200GB' = 'Google-Drive-storage' 'Google-Drive-storage-400GB' = 'Google-Drive-storage' 'Google-Drive-storage-1TB' = 'Google-Drive-storage' 'Google-Drive-storage-2TB' = 'Google-Drive-storage' 'Google-Drive-storage-4TB' = 'Google-Drive-storage' 'Google-Drive-storage-8TB' = 'Google-Drive-storage' 'Google-Drive-storage-16TB' = 'Google-Drive-storage' } } } Process { try { switch ($PSCmdlet.ParameterSetName) { Get { foreach ($U in $User) { if ($U -ceq 'me') { $U = $Script:PSGSuite.AdminEmail } elseif ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } if ($License) { Write-Verbose "Getting License SKU '$License' for User '$U'" if ($License -eq "G-Suite-Enterprise") { $License = "1010020020" } $request = $service.LicenseAssignments.Get($productHash[$License],$License,$U) $request.Execute() } else { foreach ($license in (@("G-Suite-Enterprise","Google-Apps-Unlimited","Google-Apps-For-Business","Google-Apps-For-Postini","Google-Apps-Lite","Google-Drive-storage-20GB","Google-Drive-storage-50GB","Google-Drive-storage-200GB","Google-Drive-storage-400GB","Google-Drive-storage-1TB","Google-Drive-storage-2TB","Google-Drive-storage-4TB","Google-Drive-storage-8TB","Google-Drive-storage-16TB","Google-Vault","Google-Vault-Former-Employee") | Sort-Object)) { Write-Verbose "Getting License SKU '$License' for User '$U'" if ($License -eq "G-Suite-Enterprise") { $License = "1010020020" } try { $request = $service.LicenseAssignments.Get($productHash[$License],$License,$U) $response = $request.Execute() } catch { } if ($response) { break } } if (!$response) { Write-Error "No license found for $U!" } else { return $response } } } } List { Get-GSUserLicenseListPrivate @PSBoundParameters } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSUserLicense' function Remove-GSUserLicense { <# .SYNOPSIS Removes a license assignment from a user .DESCRIPTION Removes a license assignment from a user. Useful for restoring a user from a Vault-Former-Employee to an auto-assigned G Suite Business license by removing the Vault-Former-Employee license, for example. .PARAMETER User The user's current primary email address .PARAMETER License The license SKU to remove from the user .EXAMPLE Remove-GSUserLicense -User joe -License Google-Vault-Former-Employee Removes the Vault-Former-Employee license from Joe #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [string[]] $User, [parameter(Mandatory = $true,Position = 1)] [Alias("SkuId")] [ValidateSet("G-Suite-Enterprise","Google-Apps-Unlimited","Google-Apps-For-Business","Google-Apps-For-Postini","Google-Apps-Lite","Google-Drive-storage-20GB","Google-Drive-storage-50GB","Google-Drive-storage-200GB","Google-Drive-storage-400GB","Google-Drive-storage-1TB","Google-Drive-storage-2TB","Google-Drive-storage-4TB","Google-Drive-storage-8TB","Google-Drive-storage-16TB","Google-Vault","Google-Vault-Former-Employee","1010020020")] [string] $License ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/apps.licensing' ServiceType = 'Google.Apis.Licensing.v1.LicensingService' } $service = New-GoogleService @serviceParams $productHash = @{ '1010020020' = 'Google-Apps' 'G-Suite-Enterprise' = 'Google-Apps' 'Google-Apps-Unlimited' = 'Google-Apps' 'Google-Apps-For-Business' = 'Google-Apps' 'Google-Apps-For-Postini' = 'Google-Apps' 'Google-Apps-Lite' = 'Google-Apps' 'Google-Vault' = 'Google-Vault' 'Google-Vault-Former-Employee' = 'Google-Vault' 'Google-Drive-storage-20GB' = 'Google-Drive-storage' 'Google-Drive-storage-50GB' = 'Google-Drive-storage' 'Google-Drive-storage-200GB' = 'Google-Drive-storage' 'Google-Drive-storage-400GB' = 'Google-Drive-storage' 'Google-Drive-storage-1TB' = 'Google-Drive-storage' 'Google-Drive-storage-2TB' = 'Google-Drive-storage' 'Google-Drive-storage-4TB' = 'Google-Drive-storage' 'Google-Drive-storage-8TB' = 'Google-Drive-storage' 'Google-Drive-storage-16TB' = 'Google-Drive-storage' } } Process { try { foreach ($U in $User) { if ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } if ($PSCmdlet.ShouldProcess("Revoking license '$License' from user '$U'")) { Write-Verbose "Revoking license '$License' from user '$U'" if ($License -eq "G-Suite-Enterprise") { $License = "1010020020" } $request = $service.LicenseAssignments.Delete($productHash[$License],$License,$U) $request.Execute() Write-Verbose "License revoked for user '$U'" } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Remove-GSUserLicense' function Set-GSUserLicense { <# .SYNOPSIS Sets the license for a user .DESCRIPTION Sets the license for a user .PARAMETER User The user's current primary email address .PARAMETER License The license SKU to set for the user .EXAMPLE Set-GSUserLicense -User joe -License Google-Apps-For-Business Sets Joe to a Google-Apps-For-Business license #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String[]] $User, [parameter(Mandatory = $true,Position = 1)] [Alias("SkuId")] [ValidateSet("G-Suite-Enterprise","Google-Apps-Unlimited","Google-Apps-For-Business","Google-Apps-For-Postini","Google-Apps-Lite","Google-Drive-storage-20GB","Google-Drive-storage-50GB","Google-Drive-storage-200GB","Google-Drive-storage-400GB","Google-Drive-storage-1TB","Google-Drive-storage-2TB","Google-Drive-storage-4TB","Google-Drive-storage-8TB","Google-Drive-storage-16TB","Google-Vault","Google-Vault-Former-Employee","1010020020")] [string] $License ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/apps.licensing' ServiceType = 'Google.Apis.Licensing.v1.LicensingService' } $service = New-GoogleService @serviceParams $productHash = @{ '1010020020' = 'Google-Apps' 'G-Suite-Enterprise' = 'Google-Apps' 'Google-Apps-Unlimited' = 'Google-Apps' 'Google-Apps-For-Business' = 'Google-Apps' 'Google-Apps-For-Postini' = 'Google-Apps' 'Google-Apps-Lite' = 'Google-Apps' 'Google-Vault' = 'Google-Vault' 'Google-Vault-Former-Employee' = 'Google-Vault' 'Google-Drive-storage-20GB' = 'Google-Drive-storage' 'Google-Drive-storage-50GB' = 'Google-Drive-storage' 'Google-Drive-storage-200GB' = 'Google-Drive-storage' 'Google-Drive-storage-400GB' = 'Google-Drive-storage' 'Google-Drive-storage-1TB' = 'Google-Drive-storage' 'Google-Drive-storage-2TB' = 'Google-Drive-storage' 'Google-Drive-storage-4TB' = 'Google-Drive-storage' 'Google-Drive-storage-8TB' = 'Google-Drive-storage' 'Google-Drive-storage-16TB' = 'Google-Drive-storage' } } Process { try { foreach ($U in $User) { if ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } Write-Verbose "Setting license for $U to $License" if ($License -eq "G-Suite-Enterprise") { $License = "1010020020" } $body = New-Object 'Google.Apis.Licensing.v1.Data.LicenseAssignmentInsert' -Property @{ UserId = $U } $request = $service.LicenseAssignments.Insert($body,$productHash[$License],$License) $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $U -PassThru } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Set-GSUserLicense' function Update-GSUserLicense { <# .SYNOPSIS Reassign a user's product SKU with a different SKU in the same product .DESCRIPTION Reassign a user's product SKU with a different SKU in the same product .PARAMETER User The user's current primary email address .PARAMETER License The license SKU that you would like to reassign the user to .EXAMPLE Update-GSUserLicense -User joe -License G-Suite-Enterprise Updates Joe to a G-Suite-Enterprise license #> [cmdletbinding()] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String[]] $User, [parameter(Mandatory = $false)] [Alias("SkuId")] [ValidateSet("G-Suite-Enterprise","Google-Apps-Unlimited","Google-Apps-For-Business","Google-Apps-For-Postini","Google-Apps-Lite","Google-Drive-storage-20GB","Google-Drive-storage-50GB","Google-Drive-storage-200GB","Google-Drive-storage-400GB","Google-Drive-storage-1TB","Google-Drive-storage-2TB","Google-Drive-storage-4TB","Google-Drive-storage-8TB","Google-Drive-storage-16TB","Google-Vault","Google-Vault-Former-Employee","1010020020")] [string] $License ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/apps.licensing' ServiceType = 'Google.Apis.Licensing.v1.LicensingService' } $service = New-GoogleService @serviceParams $productHash = @{ '1010020020' = 'Google-Apps' 'G-Suite-Enterprise' = 'Google-Apps' 'Google-Apps-Unlimited' = 'Google-Apps' 'Google-Apps-For-Business' = 'Google-Apps' 'Google-Apps-For-Postini' = 'Google-Apps' 'Google-Apps-Lite' = 'Google-Apps' 'Google-Vault' = 'Google-Vault' 'Google-Vault-Former-Employee' = 'Google-Vault' 'Google-Drive-storage-20GB' = 'Google-Drive-storage' 'Google-Drive-storage-50GB' = 'Google-Drive-storage' 'Google-Drive-storage-200GB' = 'Google-Drive-storage' 'Google-Drive-storage-400GB' = 'Google-Drive-storage' 'Google-Drive-storage-1TB' = 'Google-Drive-storage' 'Google-Drive-storage-2TB' = 'Google-Drive-storage' 'Google-Drive-storage-4TB' = 'Google-Drive-storage' 'Google-Drive-storage-8TB' = 'Google-Drive-storage' 'Google-Drive-storage-16TB' = 'Google-Drive-storage' } } Process { try { foreach ($U in $User) { if ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } Write-Verbose "Setting license for $U to $License" if ($License -eq "G-Suite-Enterprise") { $License = "1010020020" } $body = Get-GSUserLicense -User $U $request = $service.LicenseAssignments.Update($body,$productHash[$License],$License,$U) $request.Execute() } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Update-GSUserLicense' function Get-GSOrganizationalUnit { <# .SYNOPSIS Gets Organizational Unit information .DESCRIPTION Gets Organizational Unit information .PARAMETER SearchBase The OrgUnitPath you would like to search for. This can be the single OrgUnit to return or the top level of which to return children of .PARAMETER SearchScope The depth at which to return the list of OrgUnits children Available values are: * "Base": only return the OrgUnit specified in the SearchBase * "Subtree": return the full list of OrgUnits underneath the specified SearchBase * "OneLevel": return the SearchBase and the OrgUnit's directly underneath it * "All": same as Subtree * "Children": same as OneLevel Defaults to 'All' .EXAMPLE Get-GSOrganizationalUnit -SearchBase "/" -SearchScope Base Gets the top level Organizational Unit information #> [cmdletbinding()] Param ( [parameter(Mandatory = $false,Position = 0)] [Alias('OrgUnitPath','BaseOrgUnitPath')] [String] $SearchBase, [parameter(Mandatory = $false)] [Alias('Type')] [ValidateSet('Base','Subtree','OneLevel','All','Children')] [String] $SearchScope = 'All' ) Begin { if ($PSBoundParameters.Keys -contains 'SearchBase' -and $SearchBase -ne "/" -and $SearchScope -eq 'Base') { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.orgunit' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } } Process { try { if ($PSBoundParameters.Keys -contains 'SearchBase' -and $SearchBase -ne "/" -and $SearchScope -eq 'Base') { foreach ($O in $SearchBase) { Write-Verbose "Getting Organizational Unit '$O'" $O = $O.TrimStart('/') $request = $service.Orgunits.Get($Script:PSGSuite.CustomerId,([Google.Apis.Util.Repeatable[String]]::new([String[]]$O))) $request.Execute() } } elseif ($SearchBase -eq "/" -and $SearchScope -eq 'Base') { $topId = Get-GSOrganizationalUnitListPrivate -SearchBase "/" -Type Children -Verbose:$false | Where-Object {$_.ParentOrgUnitPath -eq "/"} | Select-Object -ExpandProperty ParentOrgUnitId -Unique Get-GSOrganizationalUnit -OrgUnitPath $topId -SearchScope Base } else { Get-GSOrganizationalUnitListPrivate @PSBoundParameters } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSOrganizationalUnit' function New-GSOrganizationalUnit { <# .SYNOPSIS Creates a new OrgUnit .DESCRIPTION Creates a new Organizational Unit .PARAMETER Name The name of the new OrgUnit .PARAMETER ParentOrgUnitPath The path of the parent OrgUnit Defaults to "/" (the root OrgUnit) .PARAMETER ParentOrgUnitId The unique ID of the parent organizational unit. .PARAMETER Description Description of the organizational unit. .PARAMETER BlockInheritance Determines if a sub-organizational unit can inherit the settings of the parent organization. The default value is false, meaning a sub-organizational unit inherits the settings of the nearest parent organizational unit. For more information on inheritance and users in an organization structure, see the administration help center: http://support.google.com/a/bin/answer.py?hl=en&answer=182442&topic=1227584&ctx=topic .EXAMPLE New-GSOrganizationalUnit -Name "Test Org" -ParentOrgUnitPath "/Testing" -Description "This is a test OrgUnit" Creates a new OrgUnit named "Test Org" underneath the existing org unit path "/Testing" with the description "This is a test OrgUnit" #> [cmdletbinding(DefaultParameterSetName = 'ParentOrgUnitPath')] Param ( [parameter(Mandatory = $true)] [String] $Name, [parameter(Mandatory = $false,ParameterSetName = 'ParentOrgUnitPath')] [string] $ParentOrgUnitPath, [parameter(Mandatory = $false,ParameterSetName = 'ParentOrgUnitId')] [string] $ParentOrgUnitId, [parameter(Mandatory = $false)] [String] $Description, [parameter(Mandatory = $false)] [Switch] $BlockInheritance ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.orgunit' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams if ($PSBoundParameters.Keys -notcontains 'ParentOrgUnitPath' -and $PSCmdlet.ParameterSetName -eq 'ParentOrgUnitPath') { $PSBoundParameters['ParentOrgUnitPath'] = '/' } } Process { try { Write-Verbose "Creating OrgUnit '$Name'" $body = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.OrgUnit' foreach ($prop in $PSBoundParameters.Keys | Where-Object {$body.PSObject.Properties.Name -contains $_}) { switch ($prop) { Default { $body.$prop = $PSBoundParameters[$prop] } } } $request = $service.Orgunits.Insert($body,$Script:PSGSuite.CustomerId) $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'New-GSOrganizationalUnit' function Remove-GSOrganizationalUnit { <# .SYNOPSIS Removes an OrgUnit .DESCRIPTION Removes an Organization Unit .PARAMETER OrgUnitPath The path of the OrgUnit you would like to Remove-GSOrganizationalUnit .EXAMPLE Remove-GSOrganizationalUnit -OrgUnitPath "/Testing" Removes the OrgUnit "/Testing" on confirmation #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $true,Position = 0)] [String[]] $OrgUnitPath ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.orgunit' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { try { foreach ($O in $OrgUnitPath) { if ($PSCmdlet.ShouldProcess("Deleting OrgUnit at Path '$O'")) { Write-Verbose "Deleting OrgUnit at Path '$O'" $O = $O.TrimStart('/') $request = $service.Orgunits.Delete($Script:PSGSuite.CustomerId,([Google.Apis.Util.Repeatable[String]]::new([String[]]$O))) $request.Execute() } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Remove-GSOrganizationalUnit' function Update-GSOrganizationalUnit { <# .SYNOPSIS Updates an OrgUnit .DESCRIPTION Updates an Organizational Unit .PARAMETER OrgUnitID The unique Id of the OrgUnit to update .PARAMETER OrgUnitPath The path of the OrgUnit to update .PARAMETER Name The new name for the OrgUnit .PARAMETER ParentOrgUnitId The new Parent ID for the OrgUnit .PARAMETER ParentOrgUnitPath The path of the new Parent for the OrgUnit .PARAMETER Description The new description for the OrgUnit .PARAMETER BlockInheritance Determines if a sub-organizational unit can inherit the settings of the parent organization. The default value is false, meaning a sub-organizational unit inherits the settings of the nearest parent organizational unit. For more information on inheritance and users in an organization structure, see the administration help center: http://support.google.com/a/bin/answer.py?hl=en&answer=182442&topic=1227584&ctx=topic .EXAMPLE Update-GSOrganizationalUnit -OrgUnitPath "/Testing" -Name "Testing More" -Description "Doing some more testing" Updates the OrgUnit '/Testing' with a new name "Testing More" and new description "Doing some more testing" #> [cmdletbinding(DefaultParameterSetName = "Id")] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true,ParameterSetName = "Id")] [ValidateNotNullOrEmpty()] [String] $OrgUnitID, [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true,ParameterSetName = "Path")] [ValidateNotNullOrEmpty()] [String] $OrgUnitPath, [parameter(Mandatory = $false)] [String] $Name, [parameter(Mandatory = $false)] [string] $ParentOrgUnitId, [parameter(Mandatory = $false)] [string] $ParentOrgUnitPath, [parameter(Mandatory = $false)] [String] $Description, [parameter(Mandatory = $false)] [Switch] $BlockInheritance ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.orgunit' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { try { $body = switch ($PSCmdlet.ParameterSetName) { Path { Get-GSOrganizationalUnit -OrgUnitPath $OrgUnitPath -Verbose:$false } Id { Get-GSOrganizationalUnit -OrgUnitPath $OrgUnitID -Verbose:$false } } if ($ParentOrgUnitPath) { $body.ParentOrgUnitId = $(Get-GSOrganizationalUnit -OrgUnitPath $ParentOrgUnitPath -Verbose:$false | Select-Object -ExpandProperty OrgUnitID) } elseif ($ParentOrgUnitId) { $body.ParentOrgUnitPath = $(Get-GSOrganizationalUnit -OrgUnitPath $ParentOrgUnitId -Verbose:$false | Select-Object -ExpandProperty OrgUnitPath) } Write-Verbose "Updating OrgUnit '$OrgUnitPath'" foreach ($prop in $PSBoundParameters.Keys | Where-Object {$body.PSObject.Properties.Name -contains $_ -and $_ -ne 'OrgUnitPath'}) { $body.$prop = $PSBoundParameters[$prop] } $trimPath = $body.OrgUnitPath.TrimStart('/') $request = $service.Orgunits.Patch($body,$Script:PSGSuite.CustomerId,([Google.Apis.Util.Repeatable[String]]::new([String[]]$trimPath))) $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Update-GSOrganizationalUnit' function Get-GSActivityReport { <# .SYNOPSIS Retrieves a list of activities .DESCRIPTION Retrieves a list of activities .PARAMETER UserKey Represents the profile id or the user email for which the data should be filtered. When 'all' is specified as the userKey, it returns usageReports for all users .PARAMETER ApplicationName Application name for which the events are to be retrieved. Available values are: * "Admin": The Admin console application's activity reports return account information about different types of administrator activity events. * "Calendar": The G Suite Calendar application's activity reports return information about various Calendar activity events. * "Drive": The Google Drive application's activity reports return information about various Google Drive activity events. The Drive activity report is only available for G Suite Business customers. * "Groups": The Google Groups application's activity reports return information about various Groups activity events. * "GPlus": The Google+ application's activity reports return information about various Google+ activity events. * "Login": The G Suite Login application's activity reports return account information about different types of Login activity events. * "Mobile": The G Suite Mobile Audit activity report return information about different types of Mobile Audit activity events. * "Rules": The G Suite Rules activity report return information about different types of Rules activity events. * "Token": The G Suite Token application's activity reports return account information about different types of Token activity events. Defaults to "Admin" .PARAMETER EventName The name of the event being queried .PARAMETER ActorIpAddress IP Address of host where the event was performed. Supports both IPv4 and IPv6 addresses .PARAMETER EndTime Return events which occurred at or before this time .PARAMETER Filters Event parameters in the form [parameter1 name][operator][parameter1 value] .PARAMETER PageSize Number of activity records to be shown in each page .EXAMPLE Get-GSActivityReport -StartTime (Get-Date).AddDays(-30) Gets the admin activity report for the last 30 days #> [cmdletbinding()] Param ( [parameter(Mandatory = $false,Position = 0)] [ValidateNotNullOrEmpty()] [String] $UserKey = 'all', [parameter(Mandatory = $false,Position = 1)] [ValidateSet("Admin","Calendar","Drive","Groups","GPlus","Login","Mobile","Rules","Token")] [String] $ApplicationName = "Admin", [parameter(Mandatory = $false,Position = 2)] [String] $EventName, [parameter(Mandatory = $false)] [DateTime] $StartTime, [parameter(Mandatory = $false)] [DateTime] $EndTime, [parameter(Mandatory = $false)] [String] $ActorIpAddress, [parameter(Mandatory = $false)] [String[]] $Filters, [parameter(Mandatory = $false)] [ValidateRange(1,1000)] [Alias("MaxResults")] [Int] $PageSize = "1000" ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.reports.audit.readonly' ServiceType = 'Google.Apis.Admin.Reports.reports_v1.ReportsService' } $service = New-GoogleService @serviceParams } Process { try { if ($UserKey -notlike "*@*.*" -and $UserKey -cne 'all') { $UserKey = "$($UserKey)@$($Script:PSGSuite.Domain)" } Write-Verbose "Getting $ApplicationName Activity report" $request = $service.Activities.List($UserKey,($ApplicationName.ToLower())) $request.MaxResults = $PageSize foreach ($key in $PSBoundParameters.Keys | Where-Object {$_ -notin @('UserKey','ApplicationName')}) { switch ($key) { StartTime { $request.$key = $StartTime.ToString('o') } EndTime { $request.$key = $EndTime.ToString('o') } Filters { $request.$key = $PSBoundParameters[$key] -join "," } Default { if ($request.PSObject.Properties.Name -contains $key) { $request.$key = $PSBoundParameters[$key] } } } } $response = @() [int]$i = 1 do { $result = $request.Execute() $response += $result.Items $request.PageToken = $result.NextPageToken [int]$retrieved = ($i + $result.Items.Count) - 1 Write-Verbose "Retrieved $retrieved events..." [int]$i = $i + $result.Items.Count } until (!$result.NextPageToken) return $response } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSActivityReport' function Get-GSUsageReport { <# .SYNOPSIS Retrieves a list of activities .DESCRIPTION Retrieves a list of activities .PARAMETER Date Represents the date for which the data is to be fetched .PARAMETER UserKey Represents the profile id or the user email for which the data should be filtered .PARAMETER EntityType Type of object. Should be one of - gplus_communities .PARAMETER EntityKey Represents the key of object for which the data should be filtered .PARAMETER Filters Represents the set of filters including parameter operator value .PARAMETER Parameters Represents the application name, parameter name pairs to fetch in csv as app_name1:param_name1 .PARAMETER PageSize Maximum number of results to return. Maximum allowed is 1000 .EXAMPLE Get-GSUsageReport -Date (Get-Date).AddDays(-30) Gets the Customer Usage report from 30 days prior #> [cmdletbinding(DefaultParameterSetName = "Customer")] Param ( [parameter(Mandatory = $true,ParameterSetName = "Customer")] [parameter(Mandatory = $true,ParameterSetName = "Entity")] [parameter(Mandatory = $true,ParameterSetName = "User")] [DateTime] $Date, [parameter(Mandatory = $true,ParameterSetName = "User")] [ValidateNotNullOrEmpty()] [String] $UserKey, [parameter(Mandatory = $true,ParameterSetName = "Entity")] [ValidateNotNullOrEmpty()] [String] $EntityType, [parameter(Mandatory = $true,ParameterSetName = "Entity")] [ValidateNotNullOrEmpty()] [String] $EntityKey, [parameter(Mandatory = $false,ParameterSetName = "Entity")] [parameter(Mandatory = $false,ParameterSetName = "User")] [String[]] $Filters, [parameter(Mandatory = $false)] [String[]] $Parameters, [parameter(Mandatory = $false,ParameterSetName = "Entity")] [parameter(Mandatory = $false,ParameterSetName = "User")] [ValidateRange(1,1000)] [Alias("MaxResults")] [Int] $PageSize = "1000" ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.reports.usage.readonly' ServiceType = 'Google.Apis.Admin.Reports.reports_v1.ReportsService' } $service = New-GoogleService @serviceParams $props = @() $props += (@{N = "Date";E = {$Date.ToString('yyyy-MM-dd')}}) } Process { try { Write-Verbose "Getting $($PSCmdlet.ParameterSetName) Usage report for $($Date.ToString('yyyy-MM-dd'))" switch ($PSCmdlet.ParameterSetName) { Customer { $request = $service.CustomerUsageReports.Get(($Date.ToString('yyyy-MM-dd'))) } Entity { $request = $service.EntityUsageReports.Get($EntityType,$EntityKey,($Date.ToString('yyyy-MM-dd'))) $request.MaxResults = $PageSize $props += (@{N = "EntityType";E = {$EntityType}}) $props += (@{N = "EntityKey";E = {$EntityKey}}) } User { if ($UserKey -ceq 'me') { $UserKey = $Script:PSGSuite.AdminEmail } elseif ($UserKey -notlike "*@*.*") { $UserKey = "$($UserKey)@$($Script:PSGSuite.Domain)" } $request = $service.UserUsageReport.Get($UserKey,($Date.ToString('yyyy-MM-dd'))) $request.MaxResults = $PageSize $props += (@{N = "UserKey";E = {$UserKey}}) } } $props += '*' foreach ($key in $PSBoundParameters.Keys | Where-Object {$_ -notin @('Date','UserKey','EntityKey','EntityType')}) { switch ($key) { Filters { $request.$key = $PSBoundParameters[$key] -join "," } Parameters { $request.$key = $PSBoundParameters[$key] -join "," } Default { if ($request.PSObject.Properties.Name -contains $key) { $request.$key = $PSBoundParameters[$key] } } } } $response = @() $warnings = @() [int]$i = 1 do { $result = $request.Execute() $response += $result.UsageReportsValue.Parameters | Select-Object $props $warnings += $result.Warnings $request.PageToken = $result.NextPageToken [int]$retrieved = ($i + $result.UsageReportsValue.Parameters.Count) - 1 Write-Verbose "Retrieved $retrieved report parameters..." [int]$i = $i + $result.UsageReportsValue.Parameters.Count } until (!$result.NextPageToken) if ($warnings) { $warnings | ForEach-Object { Write-Warning "[$($_.Code)] $($_.Message)" } } return $response } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSUsageReport' function Get-GSResource { <# .SYNOPSIS Gets Calendar Resources (Calendars, Buildings & Features supported) .DESCRIPTION Gets Calendar Resources (Calendars, Buildings & Features supported) .PARAMETER Id If Id is provided, gets the Resource by Id .PARAMETER Resource The Resource Type to List Available values are: * "Calendars": resource calendars (legacy and new - i.e. conference rooms) * "Buildings": new Building Resources (i.e. "Building A" or "North Campus") * "Features": new Feature Resources (i.e. "Video Conferencing" or "Projector") .PARAMETER Filter String query used to filter results. Should be of the form "field operator value" where field can be any of supported fields and operators can be any of supported operations. Operators include '=' for exact match and ':' for prefix match or HAS match where applicable. For prefix match, the value should always be followed by a *. Supported fields include generatedResourceName, name, buildingId, featureInstances.feature.name. For example buildingId=US-NYC-9TH AND featureInstances.feature.name:Phone. PowerShell filter syntax here is supported as "best effort". Please use Google's filter operators and syntax to ensure best results .PARAMETER OrderBy Field(s) to sort results by in either ascending or descending order. Supported fields include resourceId, resourceName, capacity, buildingId, and floorName. .PARAMETER PageSize Page size of the result set .EXAMPLE Get-GSResource -Resource Buildings Gets the full list of Buildings Resources #> [CmdletBinding(DefaultParameterSetName = "List")] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true,ParameterSetName = "Get")] [String[]] $Id, [parameter(Mandatory = $false,Position = 1)] [ValidateSet('Calendars','Buildings','Features')] [String] $Resource = 'Calendars', [parameter(Mandatory = $false,ParameterSetName = "List")] [Alias('Query')] [String[]] $Filter, [parameter(Mandatory = $false,ParameterSetName = "List")] [String[]] $OrderBy, [parameter(Mandatory = $false,ParameterSetName = "List")] [ValidateRange(1,500)] [Alias("MaxResults")] [Int] $PageSize = "500" ) Begin { if ($PSCmdlet.ParameterSetName -eq 'Get') { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.resource.calendar' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } } Process { switch ($PSCmdlet.ParameterSetName) { Get { foreach ($I in $Id) { try { Write-Verbose "Getting Resource $Resource Id '$I'" $request = $service.Resources.$Resource.Get($(if($Script:PSGSuite.CustomerID){$Script:PSGSuite.CustomerID}else{'my_customer'}),$I) $request.Execute() | Add-Member -MemberType NoteProperty -Name 'Id' -Value $I -PassThru | Add-Member -MemberType NoteProperty -Name 'Resource' -Value $Resource -PassThru | Add-Member -MemberType ScriptMethod -Name ToString -Value {$this.Id} -PassThru -Force } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } List { Get-GSResourceListPrivate @PSBoundParameters } } } } Export-ModuleMember -Function 'Get-GSResource' function New-GSResource { <# .SYNOPSIS Creates a new Calendar Resource .DESCRIPTION Creates a new Calendar Resource. Supports Resource types 'Calendars','Buildings' & 'Features' .PARAMETER Name The name of the new Resource .PARAMETER Id The unique ID for the calendar resource. .PARAMETER BuildingId Unique ID for the building a resource is located in. .PARAMETER Description Description of the resource, visible only to admins. .PARAMETER Capacity Capacity of a resource, number of seats in a room. .PARAMETER FloorName Name of the floor a resource is located on (Calendars Resource type) .PARAMETER FloorNames The names of the floors in the building (Buildings Resource type) .PARAMETER FloorSection Name of the section within a floor a resource is located in. .PARAMETER Category The category of the calendar resource. Either CONFERENCE_ROOM or OTHER. Legacy data is set to CATEGORY_UNKNOWN. Acceptable values are: * "CATEGORY_UNKNOWN" * "CONFERENCE_ROOM" * "OTHER" Defaults to 'CATEGORY_UNKNOWN' if creating a Calendar Resource .PARAMETER ResourceType The type of the calendar resource, intended for non-room resources. .PARAMETER UserVisibleDescription Description of the resource, visible to users and admins. .PARAMETER Resource The resource type you would like to create Available values are: * "Calendars": create a Resource Calendar or legacy resource type * "Buildings": create a Resource Building * "Features": create a Resource Feature .EXAMPLE New-GSResource -Name "Training Room" -Id "Train01" -Capacity 75 -Category 'CONFERENCE_ROOM' -ResourceType "Conference Room" -Description "Training room for new hires - has 1 LAN port per station" -UserVisibleDescription "Training room for new hires" Creates a new training room Resource Calendar that will be bookable on Google Calendar #> [CmdletBinding(DefaultParameterSetName = 'Features')] Param ( [parameter(Mandatory = $true,Position = 0)] [String] $Name, [parameter(Mandatory = $false,ParameterSetName = 'Calendars')] [Alias('ResourceId')] [String] $Id, [parameter(Mandatory = $false,ParameterSetName = 'Calendars')] [parameter(Mandatory = $false,ParameterSetName = 'Buildings')] [String] $BuildingId, [parameter(Mandatory = $false,ParameterSetName = 'Calendars')] [parameter(Mandatory = $false,ParameterSetName = 'Buildings')] [String] $Description, [parameter(Mandatory = $false,ParameterSetName = 'Calendars')] [Int] $Capacity, [parameter(Mandatory = $false,ParameterSetName = 'Calendars')] [String] $FloorName, [parameter(Mandatory = $false,ParameterSetName = 'Buildings')] [String[]] $FloorNames, [parameter(Mandatory = $false,ParameterSetName = 'Calendars')] [String] $FloorSection, [parameter(Mandatory = $false,ParameterSetName = 'Calendars')] [ValidateSet('CATEGORY_UNKNOWN','CONFERENCE_ROOM','OTHER')] [String] $Category, [parameter(Mandatory = $false,ParameterSetName = 'Calendars')] [String] $ResourceType, [parameter(Mandatory = $false,ParameterSetName = 'Calendars')] [String] $UserVisibleDescription, [parameter(Mandatory = $false)] [ValidateSet('Calendars','Buildings','Features')] [String] $Resource ) Begin { $resType = if ($MyInvocation.InvocationName -eq 'New-GSCalendarResource') { 'Calendars' } elseif ($Resource) { $Resource } else { $PSCmdlet.ParameterSetName } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.resource.calendar' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams if ($PSBoundParameters -notcontains 'Category' -and $PSCmdlet.ParameterSetName -eq 'Calendars') { $PSBoundParameters['Category'] = 'CATEGORY_UNKNOWN' } } Process { try { Write-Verbose "Creating Resource $resType '$Name'" $body = New-Object "$(switch ($resType) { Calendars { 'Google.Apis.Admin.Directory.directory_v1.Data.CalendarResource' } Buildings { 'Google.Apis.Admin.Directory.directory_v1.Data.Building' } Features { 'Google.Apis.Admin.Directory.directory_v1.Data.Feature' } })" foreach ($key in $PSBoundParameters.Keys) { switch ($key) { Category { $body.ResourceCategory = $PSBoundParameters[$key] } Id { if ($resType -eq 'Calendars') { $body.ResourceId = $PSBoundParameters[$key] } else { $body.$key = $PSBoundParameters[$key] } } Name { if ($resType -eq 'Calendars') { $body.ResourceName = $PSBoundParameters[$key] } elseif ($resType -eq 'Buildings') { $body.BuildingName = $PSBoundParameters[$key] } else { $body.$key = $PSBoundParameters[$key] } } Description { if ($resType -eq 'Calendars') { $body.ResourceDescription = $PSBoundParameters[$key] } else { $body.$key = $PSBoundParameters[$key] } } Default { if ($body.PSObject.Properties.Name -contains $key) { $body.$key = $PSBoundParameters[$key] } } } } $request = $service.Resources.$resType.Insert($body,$(if ($Script:PSGSuite.CustomerID) { $Script:PSGSuite.CustomerID } else { 'my_customer' })) $request.Execute() | Add-Member -MemberType NoteProperty -Name 'Resource' -Value $resType -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'New-GSResource' function Remove-GSResource { <# .SYNOPSIS Removes a resource .DESCRIPTION Removes a resource .PARAMETER ResourceId The Resource Id of the Resource *Calendar* you would like to remove .PARAMETER BuildingId The Building Id of the Resource *Building* you would like to remove .PARAMETER FeatureKey The Feature Key of the Resource *Feature* you would like to remove .EXAMPLE Remove-GSResource -ResourceId Train01 Removes the Resource Calendar 'Train01' after confirmation #> [CmdletBinding(SupportsShouldProcess = $true,ConfirmImpact = "High",DefaultParameterSetName = 'Calendars')] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true,ParameterSetName = 'Calendars')] [Alias('CalendarResourceId')] [String[]] $ResourceId, [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true,ParameterSetName = 'Buildings')] [String[]] $BuildingId, [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true,ParameterSetName = 'Features')] [Alias('Name')] [String[]] $FeatureKey ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.resource.calendar' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { try { switch ($PSCmdlet.ParameterSetName) { Calendars { $Resource = 'Calendars' $list = $ResourceId } Buildings { $Resource = 'Buildings' $list = $BuildingId } Features { $Resource = 'Features' $list = $FeatureKey } } foreach ($I in $list) { if ($PSCmdlet.ShouldProcess("Deleting Resource $Resource Id '$I'")) { Write-Verbose "Deleting Resource $Resource Id '$I'" $request = $service.Resources.$Resource.Delete($(if($Script:PSGSuite.CustomerID){$Script:PSGSuite.CustomerID}else{'my_customer'}),$I) $request.Execute() Write-Verbose "Resource $Resource Id '$I' has been successfully deleted" } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Remove-GSResource' function Update-GSResource { <# .SYNOPSIS Updates a Calendar Resource .DESCRIPTION Updates a Calendar Resource .PARAMETER ResourceId The unique Id of the Resource Calendar that you would like to update .PARAMETER BuildingId If updating a Resource Building, the unique Id of the building you would like to update If updating a Resource Calendar, the new Building Id for the resource .PARAMETER FeatureKey The unique key of the Feature you would like to update .PARAMETER Name The new name of the resource .PARAMETER Id The unique ID for the calendar resource. .PARAMETER Description Description of the resource, visible only to admins. .PARAMETER Capacity Capacity of a resource, number of seats in a room. .PARAMETER FloorName Name of the floor a resource is located on (Calendars Resource type) .PARAMETER FloorNames The names of the floors in the building (Buildings Resource type) .PARAMETER FloorSection Name of the section within a floor a resource is located in. .PARAMETER Category The new category of the calendar resource. Either CONFERENCE_ROOM or OTHER. Legacy data is set to CATEGORY_UNKNOWN. Acceptable values are: * "CATEGORY_UNKNOWN" * "CONFERENCE_ROOM" * "OTHER" Defaults to 'CATEGORY_UNKNOWN' if creating a Calendar Resource .PARAMETER ResourceType The type of the calendar resource, intended for non-room resources. .PARAMETER UserVisibleDescription Description of the resource, visible to users and admins. .PARAMETER Resource The resource type you would like to create Available values are: * "Calendars": create a Resource Calendar or legacy resource type * "Buildings": create a Resource Building * "Features": create a Resource Feature .EXAMPLE Update-GSResource -ResourceId Train01 -Id TrainingRoom01 Updates the resource Id 'Train01' to the new Id 'TrainingRoom01' #> [CmdletBinding()] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true,ParameterSetName = 'Calendars')] [Alias('CalendarResourceId')] [String] $ResourceId, [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true,ParameterSetName = 'Buildings')] [parameter(Mandatory = $false,ParameterSetName = 'Calendars')] [String] $BuildingId, [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true,ParameterSetName = 'Features')] [String] $FeatureKey, [parameter(Mandatory = $false,Position = 0)] [String] $Name, [parameter(Mandatory = $false,ParameterSetName = 'Calendars')] [String] $Id, [parameter(Mandatory = $false,ParameterSetName = 'Calendars')] [parameter(Mandatory = $false,ParameterSetName = 'Buildings')] [String] $Description, [parameter(Mandatory = $false,ParameterSetName = 'Calendars')] [Int] $Capacity, [parameter(Mandatory = $false,ParameterSetName = 'Calendars')] [String] $FloorName, [parameter(Mandatory = $false,ParameterSetName = 'Buildings')] [String[]] $FloorNames, [parameter(Mandatory = $false,ParameterSetName = 'Calendars')] [String] $FloorSection, [parameter(Mandatory = $false,ParameterSetName = 'Calendars')] [ValidateSet('CATEGORY_UNKNOWN','CONFERENCE_ROOM','OTHER')] [String] $Category, [parameter(Mandatory = $false,ParameterSetName = 'Calendars')] [String] $ResourceType, [parameter(Mandatory = $false,ParameterSetName = 'Calendars')] [String] $UserVisibleDescription, [parameter(Mandatory = $false)] [ValidateSet('Calendars','Buildings','Features')] [String] $Resource ) Begin { $resType = if ($MyInvocation.InvocationName -eq 'Update-GSCalendarResource') { 'Calendars' } elseif ($Resource) { $Resource } else { $PSCmdlet.ParameterSetName } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.resource.calendar' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams if ($PSBoundParameters -notcontains 'Category' -and $PSCmdlet.ParameterSetName -eq 'Calendars') { $PSBoundParameters['Category'] = 'CATEGORY_UNKNOWN' } } Process { try { Write-Verbose "Creating Resource $resType '$Name'" $body = New-Object "$(switch ($resType) { Calendars { 'Google.Apis.Admin.Directory.directory_v1.Data.CalendarResource' } Buildings { 'Google.Apis.Admin.Directory.directory_v1.Data.Building' } Features { 'Google.Apis.Admin.Directory.directory_v1.Data.Feature' } })" foreach ($key in $PSBoundParameters.Keys) { switch ($key) { Category { $body.ResourceCategory = $PSBoundParameters[$key] } Id { if ($resType -eq 'Calendars') { $body.ResourceId = $PSBoundParameters[$key] } else { $body.$key = $PSBoundParameters[$key] } } Name { if ($resType -eq 'Calendars') { $body.ResourceName = $PSBoundParameters[$key] } elseif ($resType -eq 'Buildings') { $body.BuildingName = $PSBoundParameters[$key] } else { $body.$key = $PSBoundParameters[$key] } } Description { if ($resType -eq 'Calendars') { $body.ResourceDescription = $PSBoundParameters[$key] } else { $body.$key = $PSBoundParameters[$key] } } Default { if ($body.PSObject.Properties.Name -contains $key) { $body.$key = $PSBoundParameters[$key] } } } } $resId = switch ($PSCmdlet.ParameterSetName) { Calendars { $ResourceId } Buildings { $BuildingId } Features { $FeatureKey } } $request = $service.Resources.$resType.Patch($body,$(if ($Script:PSGSuite.CustomerID) { $Script:PSGSuite.CustomerID } else { 'my_customer' }),$resId) $request.Execute() | Add-Member -MemberType NoteProperty -Name 'Resource' -Value $resType -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Update-GSResource' function Get-GSAdminRoleAssignment { <# .SYNOPSIS Gets a specific Admin Role Assignments or the list of Admin Role Assignments for a given role .DESCRIPTION Gets a specific Admin Role Assignments or the list of Admin Role Assignments for a given role .PARAMETER RoleAssignmentId The RoleAssignmentId(s) you would like to retrieve info for. If left blank, returns the full list of Role Assignments .PARAMETER UserKey The UserKey(s) you would like to retrieve Role Assignments for. This can be a user's email or their unique UserId If left blank, returns the full list of Role Assignments .PARAMETER RoleId The RoleId(s) you would like to retrieve Role Assignments for. If left blank, returns the full list of Role Assignments .PARAMETER PageSize Page size of the result set .EXAMPLE Get-GSAdminRoleAssignment Gets the list of Admin Role Assignments .EXAMPLE Get-GSAdminRoleAssignment -RoleId 9191482342768644,9191482342768642 Gets the Admin Role Assignments matching the provided RoleIds #> [cmdletbinding(DefaultParameterSetName = "ListUserKey")] Param ( [parameter(Mandatory = $true,Position = 0,ParameterSetName = "Get")] [String[]] $RoleAssignmentId, [parameter(Mandatory = $false,ParameterSetName = "ListUserKey")] [string[]] $UserKey, [parameter(Mandatory = $false,ParameterSetName = "ListRoleId")] [string[]] $RoleId, [parameter(Mandatory = $false,ParameterSetName = "ListUserKey")] [parameter(Mandatory = $false,ParameterSetName = "ListRoleId")] [ValidateRange(1,100)] [Alias("MaxResults")] [Int] $PageSize ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.rolemanagement.readonly' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams $customerId = if ($Script:PSGSuite.CustomerID) { $Script:PSGSuite.CustomerID } else { 'my_customer' } } Process { switch ($PSCmdlet.ParameterSetName) { Get { foreach ($Role in $RoleAssignmentId) { try { Write-Verbose "Getting Admin Role Assignment '$Role'" $request = $service.RoleAssignments.Get($customerId,$Role) $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Default { [int]$i = 1 Write-Verbose "Getting Admin Role Assignment List" $baseRequest = $service.RoleAssignments.List($customerId) if ($PSBoundParameters.Keys -contains 'PageSize') { $baseRequest.MaxResults = $PSBoundParameters['PageSize'] } if ($PSBoundParameters.Keys -contains 'RoleId' -or $PSBoundParameters.Keys -contains 'UserKey') { switch ($PSBoundParameters.Keys) { RoleId { foreach ($Role in $RoleId) { try { $request = $baseRequest $request.RoleId = $Role do { $result = $request.Execute() $result.Items | Add-Member -MemberType NoteProperty -Name 'Filter' -Value ([PSCustomObject]@{RoleId = $Role}) -PassThru $request.PageToken = $result.NextPageToken [int]$retrieved = ($i + $result.Items.Count) - 1 Write-Verbose "Retrieved $retrieved role assignments..." [int]$i = $i + $result.Items.Count } until (!$result.NextPageToken) } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } UserKey { foreach ($User in $UserKey) { try { $request = $baseRequest $uKey = try { [int64]$User } catch { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } (Get-GSUser -User $User -Verbose:$false).Id } $request.UserKey = $uKey do { $result = $request.Execute() $result.Items | Add-Member -MemberType NoteProperty -Name 'Filter' -Value ([PSCustomObject]@{UserKey = $User}) -PassThru $request.PageToken = $result.NextPageToken [int]$retrieved = ($i + $result.Items.Count) - 1 Write-Verbose "Retrieved $retrieved role assignments..." [int]$i = $i + $result.Items.Count } until (!$result.NextPageToken) } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } } else { try { $request = $baseRequest do { $result = $request.Execute() $result.Items $request.PageToken = $result.NextPageToken [int]$retrieved = ($i + $result.Items.Count) - 1 Write-Verbose "Retrieved $retrieved role assignments..." [int]$i = $i + $result.Items.Count } until (!$result.NextPageToken) } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } } } Export-ModuleMember -Function 'Get-GSAdminRoleAssignment' function New-GSAdminRoleAssignment { <# .SYNOPSIS Creates a new Admin Role Assignment .DESCRIPTION Creates a new Admin Role Assignment .PARAMETER AssignedTo The unique ID of the user this role is assigned to. .PARAMETER RoleId The ID of the role that is assigned. .PARAMETER OrgUnitId If the role is restricted to an organization unit, this contains the ID for the organization unit the exercise of this role is restricted to. .PARAMETER ScopeType The scope in which this role is assigned. Acceptable values are: * "CUSTOMER" * "ORG_UNIT" .EXAMPLE New-GSAdminRoleAssignment -AssignedTo jsmith -RoleId 9191482342768644 Assign a new role to a given user. #> [cmdletbinding()] Param ( [parameter(Mandatory = $true, Position = 0)] [String[]] $AssignedTo, [parameter(Mandatory = $true)] [Int64] $RoleId, [parameter(Mandatory = $false)] [String] $OrgUnitId, [parameter(Mandatory = $false)] [ValidateSet('CUSTOMER', 'ORG_UNIT')] [String] $ScopeType = 'CUSTOMER' ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.rolemanagement' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams $customerId = if ($Script:PSGSuite.CustomerID) { $Script:PSGSuite.CustomerID } else { 'my_customer' } } Process { foreach ($Assigned in $AssignedTo) { try { $uKey = try { [int64]$Assigned } catch { if ($Assigned -ceq 'me') { $Assigned = $Script:PSGSuite.AdminEmail } elseif ($Assigned -notlike "*@*.*") { $Assigned = "$($Assigned)@$($Script:PSGSuite.Domain)" } (Get-GSUser -User $Assigned -Verbose:$false).Id } $body = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.RoleAssignment' $body.ScopeType = $ScopeType foreach ($prop in $PSBoundParameters.Keys | Where-Object {$body.PSObject.Properties.Name -contains $_}) { switch ($prop) { AssignedTo { $body.AssignedTo = $uKey } Default { $body.$prop = $PSBoundParameters[$prop] } } } Write-Verbose "Creating Admin Role Assignment for user '$Assigned' for Role Id '$RoleId'" $request = $service.RoleAssignments.Insert($body, $customerId) $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'New-GSAdminRoleAssignment' function Remove-GSAdminRoleAssignment { <# .SYNOPSIS Removes a specific Admin Role Assignment or the list of Admin Role Assignments .DESCRIPTION Removes a specific Admin Role Assignment or the list of Admin Role Assignments .PARAMETER RoleAssignmentId The RoleAssignmentId(s) you would like to remove .EXAMPLE Remove-GSAdminRoleAssignment -RoleAssignmentId 9191482342768644,9191482342768642 Removes the role assignments matching the provided Ids #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [String[]] $RoleAssignmentId ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.rolemanagement' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams $customerId = if ($Script:PSGSuite.CustomerID) { $Script:PSGSuite.CustomerID } else { 'my_customer' } } Process { foreach ($Role in $RoleAssignmentId) { try { if ($PSCmdlet.ShouldProcess("Deleting Role Assignment Id '$Role'")) { Write-Verbose "Deleting Role Assignment Id '$Role'" $request = $service.RoleAssignments.Delete($customerId,$Role) $request.Execute() Write-Verbose "Role Assignment Id '$Role' has been successfully deleted" } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Remove-GSAdminRoleAssignment' function Get-GSAdminRole { <# .SYNOPSIS Gets a specific Admin Role or the list of Admin Roles .DESCRIPTION Gets a specific Admin Role or the list of Admin Roles .PARAMETER RoleId The RoleId(s) you would like to retrieve info for. If left blank, returns the full list of Roles .PARAMETER PageSize Page size of the result set .EXAMPLE Get-GSAdminRole Gets the list of Admin Roles .EXAMPLE Get-GSAdminRole -RoleId 9191482342768644,9191482342768642 Gets the admin roles matching the provided Ids #> [cmdletbinding(DefaultParameterSetName = "List")] Param ( [parameter(Mandatory = $true,Position = 0,ParameterSetName = "Get")] [int64[]] $RoleId, [parameter(Mandatory = $false,ParameterSetName = "List")] [ValidateRange(1,100)] [Alias("MaxResults")] [Int] $PageSize ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.rolemanagement' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams $customerId = if ($Script:PSGSuite.CustomerID) { $Script:PSGSuite.CustomerID } else { 'my_customer' } } Process { switch ($PSCmdlet.ParameterSetName) { Get { foreach ($Role in $RoleId) { try { Write-Verbose "Getting Admin Role '$Role'" $request = $service.Roles.Get($customerId,$Role) $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } List { try { Write-Verbose "Getting Admin Role List" $request = $service.Roles.List($customerId) if ($PSBoundParameters.Keys -contains 'PageSize') { $request.MaxResults = $PSBoundParameters['PageSize'] } [int]$i = 1 do { $result = $request.Execute() $result.Items $request.PageToken = $result.NextPageToken [int]$retrieved = ($i + $result.Items.Count) - 1 Write-Verbose "Retrieved $retrieved roles..." [int]$i = $i + $result.Items.Count } until (!$result.NextPageToken) } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } } Export-ModuleMember -Function 'Get-GSAdminRole' function New-GSAdminRole { <# .SYNOPSIS Creates a new Admin Role .DESCRIPTION Creates a new Admin Role .PARAMETER RoleName The name of the new role .PARAMETER RolePrivileges The set of privileges that are granted to this role. .PARAMETER RoleDescription A short description of the role. .EXAMPLE Get-GSAdminRole Gets the list of Admin Roles .EXAMPLE Get-GSAdminRole -RoleId '9191482342768644','9191482342768642' Gets the admin roles matching the provided Ids #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0)] [String] $RoleName, [parameter(Mandatory = $true,Position = 1,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Google.Apis.Admin.Directory.directory_v1.Data.Role+RolePrivilegesData[]] $RolePrivileges, [parameter(Mandatory = $false)] [String] $RoleDescription ) Begin { if ($PSCmdlet.ParameterSetName -eq 'Get') { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.rolemanagement' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } $customerId = if ($Script:PSGSuite.CustomerID) { $Script:PSGSuite.CustomerID } else { 'my_customer' } $privArray = New-Object 'System.Collections.Generic.List[Google.Apis.Admin.Directory.directory_v1.Data.Role+RolePrivilegesData]' } Process { foreach ($Privilege in $RolePrivileges) { $privArray.Add($Privilege) } } End { try { $body = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.Role' foreach ($prop in $PSBoundParameters.Keys | Where-Object {$body.PSObject.Properties.Name -contains $_}) { switch ($prop) { RolePrivileges { $body.RolePrivileges = $privArray } Default { $body.$prop = $PSBoundParameters[$prop] } } } Write-Verbose "Creating Admin Role '$RoleName' with the following privileges:`n`t- $($privArray.PrivilegeName -join "`n`t- ")" $request = $service.Roles.Insert($body,$customerId) $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'New-GSAdminRole' function Remove-GSAdminRole { <# .SYNOPSIS Removes a specific Admin Role or a list of Admin Roles .DESCRIPTION Removes a specific Admin Role or a list of Admin Roles .PARAMETER RoleId The RoleId(s) you would like to remove .EXAMPLE Remove-GSAdminRole -RoleId 9191482342768644,9191482342768642 Removes the admin roles matching the provided Ids #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [int64[]] $RoleId ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.rolemanagement' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams $customerId = if ($Script:PSGSuite.CustomerID) { $Script:PSGSuite.CustomerID } else { 'my_customer' } } Process { foreach ($Role in $RoleId) { try { if ($PSCmdlet.ShouldProcess("Deleting Role Id '$Role'")) { Write-Verbose "Deleting Role Id '$Role'" $request = $service.Roles.Delete($customerId,$Role) $request.Execute() Write-Verbose "Role Id '$Role' has been successfully deleted" } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Remove-GSAdminRole' function Update-GSAdminRole { <# .SYNOPSIS Update an Admin Role .DESCRIPTION Update an Admin Role .PARAMETER RoleId The Id of the role to update .PARAMETER RoleName The name of the role .PARAMETER RolePrivileges The set of privileges that are granted to this role. .PARAMETER RoleDescription A short description of the role. .EXAMPLE Update-GSAdminRole -RoleId 9191482342768644 -RoleName 'Help_Desk_Level2' -RoleDescription 'Help Desk Level 2' Updates the specified Admin Role with a new name and description .EXAMPLE Get-GSAdminRole | Where-Object {$_.RoleDescription -like "*Help*Desk*"} | Update-GSAdminRole -RoleId 9191482342768644 -RoleName 'Help_Desk_Level2' -RoleDescription 'Help Desk Level 2' Updates the specified Admin Role's RolePrivileges to match every other Admin Role with Help Desk in the description. Useful for basing a new role off another to add additional permissions on there #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0)] [int64] $RoleId, [parameter(Mandatory = $false)] [String] $RoleName, [parameter(Mandatory = $false,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Google.Apis.Admin.Directory.directory_v1.Data.Role+RolePrivilegesData[]] $RolePrivileges, [parameter(Mandatory = $false)] [String] $RoleDescription ) Begin { if ($PSCmdlet.ParameterSetName -eq 'Get') { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.rolemanagement' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } $customerId = if ($Script:PSGSuite.CustomerID) { $Script:PSGSuite.CustomerID } else { 'my_customer' } $privArray = New-Object 'System.Collections.Generic.List[Google.Apis.Admin.Directory.directory_v1.Data.Role+RolePrivilegesData]' } Process { foreach ($Privilege in $RolePrivileges) { $privArray.Add($Privilege) } } End { try { $body = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.Role' foreach ($prop in $PSBoundParameters.Keys | Where-Object {$_ -ne 'RoleId' -and $body.PSObject.Properties.Name -contains $_}) { switch ($prop) { RolePrivileges { $body.RolePrivileges = $privArray } Default { $body.$prop = $PSBoundParameters[$prop] } } } Write-Verbose "Updating Admin Role '$RoleId'" $request = $service.Roles.Insert($body,$customerId,$RoleId) $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Update-GSAdminRole' function Get-GSUserSchema { <# .SYNOPSIS Gets custom user schema info .DESCRIPTION Gets custom user schema info .PARAMETER SchemaId The Id or Name of the user schema you would like to return info for. If excluded, gets the full list of user schemas .EXAMPLE Get-GSUserSchema Gets the list of custom user schemas #> [cmdletbinding()] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias('Schema')] [String[]] $SchemaId ) Begin { if ($PSBoundParameters.Keys -contains 'SchemaId') { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.userschema' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } } Process { try { if ($PSBoundParameters.Keys -contains 'SchemaId') { foreach ($S in $SchemaId) { Write-Verbose "Getting schema Id '$S'" $request = $service.Schemas.Get($Script:PSGSuite.CustomerId,$S) $request.Execute() } } else { Get-GSUserSchemaListPrivate @PSBoundParameters } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSUserSchema' function New-GSUserSchema { <# .SYNOPSIS Creates a new user schema .DESCRIPTION Creates a new user schema .PARAMETER SchemaName The name of the schema to create .PARAMETER Fields New schema fields to set Expects SchemaFieldSpec objects. You can create these with the helper function Add-GSUserSchemaField, i.e.: Add-GSUserSchemaField -FieldName "date" -FieldType DATE -ReadAccessType ADMINS_AND_SELF .EXAMPLE New-GSUserSchema -SchemaName "SDK" -Fields (Add-GSUserSchemaField -FieldName "string" -FieldType STRING -ReadAccessType ADMINS_AND_SELF),(Add-GSUserSchemaField -FieldName "date" -FieldType DATE -ReadAccessType ADMINS_AND_SELF) This command will create a schema named "SDK" with two fields, "string" and "date", readable by ADMINS_AND_SELF #> [cmdletbinding()] Param ( [parameter(Mandatory = $true)] [String] $SchemaName, [parameter(Mandatory = $true)] [Google.Apis.Admin.Directory.directory_v1.Data.SchemaFieldSpec[]] $Fields ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.userschema' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { try { Write-Verbose "Creating schema '$SchemaName'" $body = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.Schema' foreach ($prop in $PSBoundParameters.Keys | Where-Object {$body.PSObject.Properties.Name -contains $_}) { switch ($prop) { Default { $body.$prop = $PSBoundParameters[$prop] } } } $request = $service.Schemas.Insert($body,$Script:PSGSuite.CustomerId) $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'New-GSUserSchema' function Remove-GSUserSchema { <# .SYNOPSIS Removes a custom user schema .DESCRIPTION Removes a custom user schema .PARAMETER SchemaId The SchemaId or SchemaName to remove. If excluded, all Custom User Schemas for the customer will be removed .EXAMPLE Remove-GSUserSchema 2SV Removes the custom user schema named '2SV' after confirmation #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias('Schema','SchemaKey','SchemaName')] [String[]] $SchemaId ) Begin { if ($PSBoundParameters.Keys -contains 'SchemaName') { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.user.userschema' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } } Process { try { if ($PSBoundParameters.Keys -contains 'SchemaId') { foreach ($S in $SchemaId) { if ($PSCmdlet.ShouldProcess("Deleting User Schema '$S'")) { Write-Verbose "Deleting User Schema '$S'" $request = $service.Schemas.Delete($Script:PSGSuite.CustomerID,$S) $request.Execute() Write-Verbose "User Schema '$S' has been successfully deleted" } } } else { if ($PSCmdlet.ShouldProcess("Deleting ALL User Schemas")) { Write-Verbose "Deleting ALL User Schemas" Get-GSUserSchema -Verbose:$false | ForEach-Object { $request = $service.Schemas.Delete($Script:PSGSuite.CustomerID,$_.SchemaId) $request.Execute() Write-Verbose "User Schema '$($_.SchemaId)' has been successfully deleted" } } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Remove-GSUserSchema' function Set-GSUserSchema { <# .SYNOPSIS Hard-sets a schema's configuration .DESCRIPTION Hard-sets a schema's configuration .PARAMETER SchemaId The unique Id of the schema to set .PARAMETER SchemaName The new schema name .PARAMETER Fields New schema fields to set Expects SchemaFieldSpec objects. You can create these with the helper function Add-GSUserSchemaField, i.e.: Add-GSUserSchemaField -FieldName "date" -FieldType DATE -ReadAccessType ADMINS_AND_SELF .EXAMPLE Set-GSUserSchema -SchemaId "9804800jfl08917304j" -SchemaName "SDK_2" -Fields (Add-GSUserSchemaField -FieldName "string2" -FieldType STRING -ReadAccessType ADMINS_AND_SELF) This command will set the schema Id '9804800jfl08917304j' with the name "SDK_2" and one field "string2" readable by ADMINS_AND_SELF #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias('Schema')] [String] $SchemaId, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [String] $SchemaName, [parameter(Mandatory = $false)] [Google.Apis.Admin.Directory.directory_v1.Data.SchemaFieldSpec[]] $Fields ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.userschema' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { try { Write-Verbose "Setting schema '$SchemaId'" $schemaObj = Get-GSUserSchema -Schema $SchemaId -Verbose:$false $body = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.Schema' foreach ($prop in $PSBoundParameters.Keys | Where-Object {$body.PSObject.Properties.Name -contains $_}) { $body.$prop = $PSBoundParameters[$prop] } $request = $service.Schemas.Update($body,$Script:PSGSuite.CustomerId,$schemaObj.SchemaId) $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Set-GSUserSchema' function Update-GSUserSchema { <# .SYNOPSIS Updates/patches a schema's configuration .DESCRIPTION Updates/patches a schema's configuration .PARAMETER SchemaId The unique Id of the schema to update .PARAMETER SchemaName The new schema name .PARAMETER Fields New schema fields to set Expects SchemaFieldSpec objects. You can create these with the helper function Add-GSUserSchemaField, i.e.: Add-GSUserSchemaField -FieldName "date" -FieldType DATE -ReadAccessType ADMINS_AND_SELF .EXAMPLE Update-GSUserSchema -SchemaId "9804800jfl08917304j" -SchemaName "SDK_2" -Fields (Add-GSUserSchemaField -FieldName "string2" -FieldType STRING -ReadAccessType ADMINS_AND_SELF) This command will update the schema Id '9804800jfl08917304j' with the name "SDK_2" and add one field "string2" readable by ADMINS_AND_SELF #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias('Schema')] [String] $SchemaId, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [String] $SchemaName, [parameter(Mandatory = $false)] [Google.Apis.Admin.Directory.directory_v1.Data.SchemaFieldSpec[]] $Fields ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.userschema' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { try { Write-Verbose "Updating schema '$SchemaId'" $schemaObj = Get-GSUserSchema -Schema $SchemaId -Verbose:$false $body = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.Schema' foreach ($prop in $PSBoundParameters.Keys | Where-Object {$body.PSObject.Properties.Name -contains $_}) { $body.$prop = $PSBoundParameters[$prop] } $request = $service.Schemas.Patch($body,$Script:PSGSuite.CustomerId,$schemaObj.SchemaId) $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Update-GSUserSchema' function Get-GSMobileDevice { <# .SYNOPSIS Gets the list of Mobile Devices registered for the user's account .DESCRIPTION Gets the list of Mobile Devices registered for the user's account .PARAMETER User The user that you would like to retrieve the Mobile Device list for. If no user is specified, it will list all of the Mobile Devices of the CustomerID .PARAMETER Filter Search string in the format given at: http://support.google.com/a/bin/answer.py?hl=en&answer=1408863#search .PARAMETER Projection Restrict information returned to a set of selected fields. Acceptable values are: * "BASIC": Includes only the basic metadata fields (e.g., deviceId, model, status, type, and status) * "FULL": Includes all metadata fields Defauls to "FULL" .PARAMETER PageSize Page size of the result set .PARAMETER OrderBy Device property to use for sorting results. Acceptable values are: * "deviceId": The serial number for a Google Sync mobile device. For Android devices, this is a software generated unique identifier. * "email": The device owner's email address. * "lastSync": Last policy settings sync date time of the device. * "model": The mobile device's model. * "name": The device owner's user name. * "os": The device's operating system. * "status": The device status. * "type": Type of the device. .PARAMETER SortOrder Whether to return results in ascending or descending order. Must be used with the OrderBy parameter. Acceptable values are: * "ASCENDING": Ascending order. * "DESCENDING": Descending order. .EXAMPLE Get-GSMobileDevice Gets the Mobile Device list for the AdminEmail #> [cmdletbinding(DefaultParameterSetName = "User")] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true,ParameterSetName = "User")] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String[]] $User, [parameter(Mandatory = $false,ParameterSetName = "Query",Position = 0)] [Alias('Query')] [String] $Filter, [parameter(Mandatory = $false)] [ValidateSet("BASIC","FULL")] [String] $Projection = "FULL", [parameter(Mandatory = $false)] [ValidateRange(1,1000)] [Int] $PageSize = "1000", [parameter(Mandatory = $false)] [ValidateSet("deviceId","email","lastSync","model","name","os","status","type")] [String] $OrderBy, [parameter(Mandatory = $false)] [ValidateSet("Ascending","Descending")] [String] $SortOrder ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.device.mobile' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { try { $request = $service.Mobiledevices.List($Script:PSGSuite.CustomerID) switch ($PSCmdlet.ParameterSetName) { User { if ($User) { foreach ($U in $User) { if ($U -ceq 'me') { $U = $Script:PSGSuite.AdminEmail } elseif ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } $Filter = "email:`"$U`"" $request.Query = $Filter Write-Verbose "Getting Mobile Device list for User '$U'" $response = @() [int]$i = 1 do { $result = $request.Execute() $response += $result.Mobiledevices if ($result.NextPageToken) { $request.PageToken = $result.NextPageToken } [int]$retrieved = ($i + $result.Mobiledevices.Count) - 1 Write-Verbose "Retrieved $retrieved Mobile Devices..." [int]$i = $i + $result.Mobiledevices.Count } until (!$result.NextPageToken) return $response } } else { Write-Verbose "Getting Mobile Device list for customer '$($script:PSGSuite.CustomerID)'" $response = @() [int]$i = 1 do { $result = $request.Execute() $response += $result.Mobiledevices if ($result.NextPageToken) { $request.PageToken = $result.NextPageToken } [int]$retrieved = ($i + $result.Mobiledevices.Count) - 1 Write-Verbose "Retrieved $retrieved Mobile Devices..." [int]$i = $i + $result.Mobiledevices.Count } until (!$result.NextPageToken) return $response } } Query { $request.Query = $Filter Write-Verbose "Getting Mobile Device list for filter '$Filter'" $response = @() [int]$i = 1 do { $result = $request.Execute() $response += $result.Mobiledevices if ($result.NextPageToken) { $request.PageToken = $result.NextPageToken } [int]$retrieved = ($i + $result.Mobiledevices.Count) - 1 Write-Verbose "Retrieved $retrieved Mobile Devices..." [int]$i = $i + $result.Mobiledevices.Count } until (!$result.NextPageToken) return $response } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSMobileDevice' function Get-GSUserASP { <# .SYNOPSIS Gets Application Specific Passwords for a user .DESCRIPTION Gets Application Specific Passwords for a user .PARAMETER User The primary email or UserID of the user who you are trying to get info for. You can exclude the '@domain.com' to insert the Domain in the config or use the special 'me' to indicate the AdminEmail in the config. Defaults to the AdminEmail in the config .PARAMETER CodeId The ID of the ASP you would like info for. If excluded, returns the full list of ASP's for the user .EXAMPLE Get-GSUserASP Gets the list of Application Specific Passwords for the user #> [cmdletbinding()] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String[]] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false,Position = 1,ValueFromPipelineByPropertyName = $true)] [ValidateNotNullOrEmpty()] [String] $CodeId ) Begin { if ($PSBoundParameters.Keys -contains 'CodeId') { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.user.security' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } } Process { try { foreach ($U in $User) { if ($U -ceq 'me') { $U = $Script:PSGSuite.AdminEmail } elseif ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } if ($PSBoundParameters.Keys -contains 'CodeId') { Write-Verbose "Getting ASP '$CodeId' for User '$U'" $request = $service.Asps.Get($U,$CodeId) $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $U -PassThru } else { $PSBoundParameters['User'] = $U Get-GSUserASPListPrivate @PSBoundParameters } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSUserASP' function Get-GSUserToken { <# .SYNOPSIS Gets security tokens for a user .DESCRIPTION Gets security tokens for a user .PARAMETER User The primary email or UserID of the user who you are trying to get info for. You can exclude the '@domain.com' to insert the Domain in the config or use the special 'me' to indicate the AdminEmail in the config. Defaults to the AdminEmail in the config .PARAMETER ClientId The Id of the client you are trying to get token info for. If excluded, gets the full list of tokens for the user .EXAMPLE Get-GSUserToken -ClientId "Google Chrome" Gets the token info for "Google Chrome" for the AdminEmail user #> [cmdletbinding()] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String[]] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false,Position = 1,ValueFromPipelineByPropertyName = $true)] [ValidateNotNullOrEmpty()] [String] $ClientId ) Begin { if ($PSBoundParameters.Keys -contains 'ClientId') { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.user.security' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } } Process { try { foreach ($U in $User) { if ($U -ceq 'me') { $U = $Script:PSGSuite.AdminEmail } elseif ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } if ($PSBoundParameters.Keys -contains 'ClientId') { Write-Verbose "Getting Token '$ClientId' for User '$U'" $request = $service.Tokens.Get($U,$ClientId) $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $U -PassThru } else { $PSBoundParameters['User'] = $U Get-GSUserTokenListPrivate @PSBoundParameters } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSUserToken' function Get-GSUserVerificationCodes { <# .SYNOPSIS Gets the 2-Step Verification Codes for the user .DESCRIPTION Gets the 2-Step Verification Codes for the user .PARAMETER User The primary email or UserID of the user who you are trying to get info for. You can exclude the '@domain.com' to insert the Domain in the config or use the special 'me' to indicate the AdminEmail in the config. Defaults to the AdminEmail in the config .EXAMPLE Get-GSUserVerificationCodes Gets the Verification Codes for AdminEmail user #> [cmdletbinding()] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String[]] $User = $Script:PSGSuite.AdminEmail ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.user.security' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { try { foreach ($U in $User) { if ($U -ceq 'me') { $U = $Script:PSGSuite.AdminEmail } elseif ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } Write-Verbose "Getting Verification Code list for User '$U'" $request = $service.VerificationCodes.List($U) $request.Execute() | Select-Object -ExpandProperty Items | Add-Member -MemberType NoteProperty -Name 'User' -Value $U -PassThru } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSUserVerificationCodes' function New-GSUserVerificationCodes { <# .SYNOPSIS Generates new verification codes for the user .DESCRIPTION Generates new verification codes for the user .PARAMETER User The primary email or UserID of the user who you are trying to get info for. You can exclude the '@domain.com' to insert the Domain in the config or use the special 'me' to indicate the AdminEmail in the config .EXAMPLE New-GSUserVerificationCodes -User me Generates new verification codes for the AdminEmail user #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String[]] $User ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.user.security' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { try { foreach ($U in $User) { if ($U -ceq 'me') { $U = $Script:PSGSuite.AdminEmail } elseif ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } Write-Verbose "Generating new verification codes for user '$U'" $request = $service.VerificationCodes.Generate($U) $request.Execute() Write-Verbose "New verification codes successfully generated for user '$U'" } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'New-GSUserVerificationCodes' function Remove-GSMobileDevice { <# .SYNOPSIS Removes a mobile device from Device Management .DESCRIPTION Removes a mobile device from Device Management .PARAMETER ResourceID The unique Id of the mobile device you would like to remove .EXAMPLE Remove-GSMobileDevice -ResourceId 'AFiQxQ8Qgd-rouSmcd2UnuvhYV__WXdacTgJhPEA1QoQJrK1hYbKJXm-8JFlhZOjBF4aVbhleS2FVQk5lI069K2GULpteTlLVpKLJFSLSL' Removes the mobile device with the specified Id #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)] [ValidateNotNullOrEmpty()] [String[]] $ResourceId ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.device.mobile' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { try { foreach ($R in $ResourceId) { if ($PSCmdlet.ShouldProcess("Removing Mobile Device '$R'")) { Write-Verbose "Removing Mobile Device '$R'" $request = $service.Mobiledevices.Delete($Script:PSGSuite.CustomerID,$R) $request.Execute() Write-Verbose "Mobile Device '$R' has been successfully removed" } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Remove-GSMobileDevice' function Remove-GSUserASP { <# .SYNOPSIS Removes an Application Specific Password for a user .DESCRIPTION Removes an Application Specific Password for a user .PARAMETER User The user to remove ASPs ValueFromPipeline .PARAMETER CodeId The ASP Code Id to remove. If excluded, all ASPs for the user will be removed .EXAMPLE Remove-GSUserASP -User joe Removes *ALL* ASPs from joe@domain.com's account after confirmation #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String[]] $User, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [String[]] $CodeId ) Begin { if ($PSBoundParameters.Keys -contains 'CodeId') { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.user.security' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } } Process { try { foreach ($U in $User) { if ($U -ceq 'me') { $U = $Script:PSGSuite.AdminEmail } elseif ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } if ($PSBoundParameters.Keys -contains 'CodeId') { foreach ($C in $CodeId) { if ($PSCmdlet.ShouldProcess("Deleting ASP CodeId '$C' for user '$U'")) { Write-Verbose "Deleting ASP CodeId '$C' for user '$U'" $request = $service.Asps.Delete($U,$C) $request.Execute() Write-Verbose "ASP CodeId '$C' has been successfully deleted for user '$U'" } } } else { if ($PSCmdlet.ShouldProcess("Deleting ALL ASPs for user '$U'")) { Write-Verbose "Deleting ALL ASPs for user '$U'" Get-GSUserASP -User $U -Verbose:$false | ForEach-Object { $request = $service.Asps.Delete($U,$_.CodeId) $request.Execute() Write-Verbose "ASP CodeId '$($_.CodeId)' has been successfully deleted for user '$U'" } } } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Remove-GSUserASP' function Remove-GSUserToken { <# .SYNOPSIS Removes a security token from a user .DESCRIPTION Removes a security token from a user .PARAMETER User The user to remove the security token from .PARAMETER ClientID The client Id of the security token. If excluded, all security tokens for the user are removed .EXAMPLE An example .NOTES General notes #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String[]] $User, [parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)] [String[]] $ClientID ) Begin { if ($PSBoundParameters.Keys -contains 'ClientID') { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.user.security' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } } Process { try { foreach ($U in $User) { if ($U -ceq 'me') { $U = $Script:PSGSuite.AdminEmail } elseif ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } if ($PSBoundParameters.Keys -contains 'ClientID') { foreach ($C in $ClientID) { if ($PSCmdlet.ShouldProcess("Deleting Token ClientID '$C' for user '$U'")) { Write-Verbose "Deleting Token ClientID '$C' for user '$U'" $request = $service.Tokens.Delete($U,$C) $request.Execute() Write-Verbose "Token ClientID '$C' has been successfully deleted for user '$U'" } } } else { if ($PSCmdlet.ShouldProcess("Deleting ALL tokens for user '$U'")) { Write-Verbose "Deleting ALL tokens for user '$U'" Get-GSUserToken -User $U -Verbose:$false | ForEach-Object { $request = $service.Tokens.Delete($U,$_.ClientID) $request.Execute() Write-Verbose "Token ClientID '$($_.ClientID)' has been successfully deleted for user '$U'" } } } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Remove-GSUserToken' function Revoke-GSUserVerificationCodes { <# .SYNOPSIS Revokes/invalidates Verification Codes for the user .DESCRIPTION Revokes/invalidates Verification Codes for the user .PARAMETER User The user to revoke verification codes from .EXAMPLE Revoke-GSUserVerificationCodes -User me -Confirm:$false Invalidates the verification codes for the AdminEmail user, skipping confirmation #> [cmdletbinding()] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String[]] $User ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.user.security' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { try { foreach ($U in $User) { if ($U -ceq 'me') { $U = $Script:PSGSuite.AdminEmail } elseif ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } Write-Verbose "Invalidating verification codes for user '$U'" $request = $service.VerificationCodes.Invalidate($U) $request.Execute() Write-Verbose "Verification codes successfully invalidated for user '$U'" } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Revoke-GSUserVerificationCodes' function Update-GSMobileDevice { <# .SYNOPSIS Updates a mobile device with the action specified .DESCRIPTION Updates a mobile device with the action specified .PARAMETER ResourceID The unique Id of the mobile device you would like to update .PARAMETER Action The action to be performed on the device. Acceptable values are: * "admin_account_wipe": Remotely wipes only G Suite data from the device. See the administration help center for more information. * "admin_remote_wipe": Remotely wipes all data on the device. See the administration help center for more information. * "approve": Approves the device. If you've selected Enable device activation, devices that register after the device activation setting is enabled will need to be approved before they can start syncing with your domain. Enabling device activation forces the device user to install the Device Policy app to sync with G Suite. * "block": Blocks access to G Suite data (mail, calendar, and contacts) on the device. The user can still access their mail, calendar, and contacts from a desktop computer or mobile browser. * "cancel_remote_wipe_then_activate": Cancels a remote wipe of the device and then reactivates it. * "cancel_remote_wipe_then_block": Cancels a remote wipe of the device and then blocks it. .EXAMPLE Update-GSMobileDevice -ResourceId 'AFiQxQ8Qgd-rouSmcd2UnuvhYV__WXdacTgJhPEA1QoQJrK1hYbKJXm-8JFlhZOjBF4aVbhleS2FVQk5lI069K2GULpteTlLVpKLJFSLSL' -Action approve Approves the mobile device with the specified Id #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)] [ValidateNotNullOrEmpty()] [String[]] $ResourceId, [parameter(Mandatory = $true)] [ValidateSet('admin_account_wipe','admin_remote_wipe','approve','block','cancel_remote_wipe_then_activate','cancel_remote_wipe_then_block')] [String] $Action ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.device.mobile' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams $customerId = if ($Script:PSGSuite.CustomerID) { $Script:PSGSuite.CustomerID } else { "my_customer" } $body = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.MobileDeviceAction' -Property @{ Action = $Action } } Process { try { foreach ($R in $ResourceId) { Write-Verbose "Updating Mobile Device '$R' with Action '$Action'" $request = $service.Mobiledevices.Action($body,$customerId,$R) $request.Execute() Write-Verbose "Mobile Device was successfully updated" } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Update-GSMobileDevice' function Clear-GSSheet { <# .SYNOPSIS Clears a Sheet .DESCRIPTION Clears a Sheet .PARAMETER SpreadsheetId The unique Id of the SpreadSheet .PARAMETER SheetName The name of the Sheet (tab) to clear .PARAMETER Range The specific range to clear. If excluded, clears the entire Sheet .PARAMETER User The primary email of the user who has Edit rights to the target Range/Sheet .PARAMETER Raw If $true, return the raw response, otherwise, return a flattened response for readability .EXAMPLE Clear-GSSheet -SpreadsheetId '1ZVdewVhy-VtVLyGL1lk2kgvySIF_bCfJA6ggn7obGh2U' -SheetName 2017 Clears the Sheet '2017' located on the SpreadSheet Id provided #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0)] [String] $SpreadsheetId, [parameter(Mandatory = $false)] [String] $SheetName, [parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [Alias('SpecifyRange')] [string] $Range, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias('Owner','PrimaryEmail','UserKey','Mail')] [string] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false)] [switch] $Raw ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/drive' ServiceType = 'Google.Apis.Sheets.v4.SheetsService' User = $User } $service = New-GoogleService @serviceParams } Process { try { if ($SheetName) { if ($Range -like "'*'!*") { throw "SpecifyRange formatting error! When using the SheetName parameter, please exclude the SheetName when formatting the SpecifyRange value (i.e. 'A1:Z1000')" } elseif ($Range) { $Range = "'$($SheetName)'!$Range" } else { $Range = "$SheetName" } } $body = New-Object 'Google.Apis.Sheets.v4.Data.ClearValuesRequest' $request = $service.Spreadsheets.Values.Clear($body,$SpreadsheetId,$Range) Write-Verbose "Clearing range '$Range' on Sheet '$SpreadsheetId' for user '$User'" $response = $request.Execute() if (!$Raw) { $response = $response | Select-Object @{N = "Title";E = {$_.properties.title}},@{N = "MaxRows";E = {[int]($_.sheets.properties.gridProperties.rowCount | Sort-Object | Select-Object -Last 1)}},@{N = "MaxColumns";E = {[int]($_.sheets.properties.gridProperties.columnCount | Sort-Object | Select-Object -Last 1)}},* } $response | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Clear-GSSheet' function Copy-GSSheet { <# .SYNOPSIS Copies a Sheet from one SpreadSheet to another .DESCRIPTION Copies a Sheet from one SpreadSheet to another .PARAMETER SourceSpreadsheetId The unique Id of the SpreadSheet to copy the Sheet from .PARAMETER SourceSheetId The Id of the Sheet to copy .PARAMETER DestinationSpreadsheetId The target SpreadSheet to copy the Sheet to .PARAMETER NewSheetTitle The new title for the new SpreadhSheet to create if not copying to a Destination Sheet .PARAMETER User The primary email of the user who has at least Edit rights to both the Source SpreadSheet and Destination SpreadSheet .PARAMETER Raw If $true, return the raw response, otherwise, return a flattened response for readability .EXAMPLE Copy-GSSheet -SourceSpreadsheetId '1ZVdewVhy-VtVLyGLhClkj8234ljk_fJA6ggn7obGh2U' -SourceSheetId 2017 -NewSheetTitle '2017 Archive' Copies the Sheet '2017' from the SourceSpreadsheet provided onto a new SpreadSheet named '2017 Archive' #> [cmdletbinding(DefaultParameterSetName = "CreateNewSheet")] Param ( [parameter(Mandatory = $true,Position = 0)] [String] $SourceSpreadsheetId, [parameter(Mandatory = $true,Position = 1)] [String] $SourceSheetId, [parameter(Mandatory = $true,Position = 2,ParameterSetName = "UseExisting")] [String] $DestinationSpreadsheetId, [parameter(Mandatory = $false,ParameterSetName = "CreateNewSheet")] [Alias('SheetTitle')] [String] $NewSheetTitle, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias('Owner','PrimaryEmail','UserKey','Mail')] [string] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false)] [switch] $Raw ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/drive' ServiceType = 'Google.Apis.Sheets.v4.SheetsService' User = $User } $service = New-GoogleService @serviceParams } Process { try { if ($PSCmdlet.ParameterSetName -eq "CreateNewSheet") { if ($NewSheetTitle) { Write-Verbose "Creating new spreadsheet titled: $NewSheetTitle" } else { Write-Verbose "Creating new untitled spreadsheet" } $DestinationSpreadsheetId = New-GSSheet -Title $NewSheetTitle -User $User -Verbose:$false | Select-Object -ExpandProperty SpreadsheetId Write-Verbose "New spreadsheet ID: $DestinationSpreadsheetId" } $body = New-Object 'Google.Apis.Sheets.v4.Data.CopySheetToAnotherSpreadsheetRequest' -Property @{ DestinationSpreadsheetId = $DestinationSpreadsheetId } Write-Verbose "Copying Sheet '$SourceSheetId' from Spreadsheet '$SourceSpreadsheetId' to Spreadsheet '$DestinationSpreadsheetId' for user '$User'" $request = $service.Spreadsheets.Sheets.CopyTo($body,$SourceSpreadsheetId,$SourceSheetId) $response = $request.Execute() if (!$Raw) { $response = $response | Select-Object @{N = "Title";E = {$_.properties.title}},@{N = "MaxRows";E = {[int]($_.sheets.properties.gridProperties.rowCount | Sort-Object | Select-Object -Last 1)}},@{N = "MaxColumns";E = {[int]($_.sheets.properties.gridProperties.columnCount | Sort-Object | Select-Object -Last 1)}},* } $response | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Copy-GSSheet' function Export-GSSheet { <# .SYNOPSIS Updates a Sheet's values .DESCRIPTION Updates a Sheet's values. Accepts either an Array of objects/strings/ints or a single value .PARAMETER SpreadsheetId The unique Id of the SpreadSheet to update if updating an existing Sheet .PARAMETER NewSheetTitle The title of the new SpreadSheet to be created .PARAMETER Array Array of objects/strings/ints to add to the SpreadSheet .PARAMETER Value A single value to update 1 cell with. Useful if you are tracking the last time updated in a specific cell during a job that updates Sheets .PARAMETER SheetName The name of the Sheet to add the data to. If excluded, defaults to Sheet Id '0'. If a new SpreadSheet is being created, this is set to 'Sheet1' to prevent error .PARAMETER Style The table style you would like to export the data as Available values are: * "Standard": headers are on Row 1, table rows are added as subsequent rows (Default) * "Horizontal": headers are on Column A, table rows are added as subsequent columns .PARAMETER Range The specific range to add the value(s) to. If using the -Value parameter, set this to the specific cell you would like to set the value of .PARAMETER Append If $true, skips adding headers to the Sheet .PARAMETER User The primary email of the user that had at least Edit rights to the target Sheet Defaults to the AdminEmail user .PARAMETER ValueInputOption How the input data should be interpreted Available values are: * "INPUT_VALUE_OPTION_UNSPECIFIED" * "RAW" * "USER_ENTERED" .PARAMETER IncludeValuesInResponse Determines if the update response should include the values of the cells that were updated. By default, responses do not include the updated values .PARAMETER Launch If $true, opens the new SpreadSheet Url in your default browser .EXAMPLE $array | Export-GSSheet -NewSheetTitle "Finance Workbook" -Launch #> [cmdletbinding(DefaultParameterSetName = "CreateNewSheetArray")] Param ( [parameter(Mandatory = $true,Position = 0,ParameterSetName = "UseExistingArray")] [parameter(Mandatory = $true,Position = 0,ParameterSetName = "UseExistingValue")] [String] $SpreadsheetId, [parameter(Mandatory = $false,Position = 0,ParameterSetName = "CreateNewSheetArray")] [parameter(Mandatory = $false,Position = 0,ParameterSetName = "CreateNewSheetValue")] [String] $NewSheetTitle, [parameter(Mandatory = $true,Position = 1,ValueFromPipeline = $true,ParameterSetName = "UseExistingArray")] [parameter(Mandatory = $true,Position = 1,ValueFromPipeline = $true,ParameterSetName = "CreateNewSheetArray")] [object[]] $Array, [parameter(Mandatory = $true,Position = 1,ParameterSetName = "UseExistingValue")] [parameter(Mandatory = $true,Position = 1,ParameterSetName = "CreateNewSheetValue")] [string] $Value, [parameter(Mandatory = $false)] [String] $SheetName, [parameter(Mandatory = $false,ParameterSetName = "UseExistingArray")] [parameter(Mandatory = $false,ParameterSetName = "CreateNewSheetArray")] [ValidateSet('Standard','Horizontal')] [String] $Style = "Standard", [parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [Alias('SpecifyRange')] [string] $Range, [parameter(Mandatory = $false)] [switch] $Append, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias('Owner','PrimaryEmail','UserKey','Mail')] [string] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false)] [ValidateSet("INPUT_VALUE_OPTION_UNSPECIFIED","RAW","USER_ENTERED")] [string] $ValueInputOption = "RAW", [parameter(Mandatory = $false)] [Switch] $IncludeValuesInResponse, [parameter(Mandatory = $false)] [Alias('Open')] [Switch] $Launch ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/drive' ServiceType = 'Google.Apis.Sheets.v4.SheetsService' User = $User } $service = New-GoogleService @serviceParams $values = New-Object 'System.Collections.Generic.List[System.Collections.Generic.IList[Object]]' } Process { try { if ($Value) { $finalArray = $([pscustomobject]@{Value = "$Value"}) $Append = $true } else { if (!$contentType) { $contentType = $Array[0].PSObject.TypeNames[0] } $finalArray = @() if ($contentType -eq 'System.String' -or $contentType -like "System.Int*") { $Append = $true foreach ($item in $Array) { $finalArray += $([pscustomobject]@{Value = $item}) } } else { foreach ($item in $Array) { $finalArray += $item } } } if (!$Append) { $propArray = New-Object 'System.Collections.Generic.List[Object]' $finalArray[0].PSObject.Properties.Name | ForEach-Object { $propArray.Add($_) } $values.Add([System.Collections.Generic.IList[Object]]$propArray) $Append = $true } foreach ($object in $finalArray) { $valueArray = New-Object 'System.Collections.Generic.List[Object]' $object.PSobject.Properties.Value | ForEach-Object { $valueArray.Add($_) } $values.Add([System.Collections.Generic.IList[Object]]$valueArray) } } catch { $PSCmdlet.ThrowTerminatingError($_) } } End { try { if ($PSCmdlet.ParameterSetName -like "CreateNewSheet*") { if ($NewSheetTitle) { Write-Verbose "Creating new spreadsheet titled: $NewSheetTitle" } else { Write-Verbose "Creating new untitled spreadsheet" } $sheet = New-GSSheet -Title $NewSheetTitle -User $User -Verbose:$false $SpreadsheetId = $sheet.SpreadsheetId $SpreadsheetUrl = $sheet.SpreadsheetUrl $SheetName = 'Sheet1' Write-Verbose "New spreadsheet ID: $SpreadsheetId" } else { $sheet = Get-GSSheetInfo -SpreadsheetId $SpreadsheetId -User $User -Verbose:$false $SpreadsheetUrl = $sheet.SpreadsheetUrl } if ($SheetName) { if ($Range -like "'*'!*") { throw "SpecifyRange formatting error! When using the SheetName parameter, please exclude the SheetName when formatting the SpecifyRange value (i.e. 'A1:Z1000')" } elseif ($Range) { $Range = "'$($SheetName)'!$Range" } else { $Range = "$SheetName" } } $bodyData = (New-Object 'Google.Apis.Sheets.v4.Data.ValueRange' -Property @{ Range = $Range MajorDimension = "$(if($Style -eq 'Horizontal'){'COLUMNS'}else{'ROWS'})" Values = [System.Collections.Generic.IList[System.Collections.Generic.IList[Object]]]$values }) $body = New-Object 'Google.Apis.Sheets.v4.Data.BatchUpdateValuesRequest' $body.ValueInputOption = $ValueInputOption $body.IncludeValuesInResponse = $IncludeValuesInResponse $body.Data = [Google.Apis.Sheets.v4.Data.ValueRange[]]$bodyData $request = $service.Spreadsheets.Values.BatchUpdate($body,$SpreadsheetId) Write-Verbose "Updating Range '$Range' on Spreadsheet '$SpreadsheetId' for user '$User'" $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru | Add-Member -MemberType NoteProperty -Name 'SpreadsheetUrl' -Value $SpreadsheetUrl -PassThru if ($Launch) { Write-Verbose "Launching new spreadsheet at $SpreadsheetUrl" Start-Process $SpreadsheetUrl } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Export-GSSheet' function Get-GSSheetInfo { <# .SYNOPSIS Gets metadata about a SpreadSheet .DESCRIPTION Gets metadata about a SpreadSheet .PARAMETER SpreadsheetId The unique Id of the SpreadSheet to retrieve info for .PARAMETER User The owner of the SpreadSheet .PARAMETER SheetName The name of the Sheet to retrieve info for .PARAMETER Range The specific range of the Sheet to retrieve info for .PARAMETER IncludeGridData Whether or not to include Grid Data in the response .PARAMETER Fields The fields to return in the response Available values are: * "namedRanges" * "properties" * "sheets" * "spreadsheetId" .PARAMETER Raw If $true, return the raw response, otherwise, return a flattened response for readability .EXAMPLE Get-GSSheetInfo -SpreadsheetId '1rhsAYTOB_vrpvfwImPmWy0TcVa2sgmQa_9u976' Gets the info for the SpreadSheet provided #> [cmdletbinding()] Param ( [parameter(Mandatory = $true)] [String] $SpreadsheetId, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias('Owner','PrimaryEmail','UserKey','Mail')] [string] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false)] [String] $SheetName, [parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [Alias('SpecifyRange')] [string] $Range, [parameter(Mandatory = $false)] [Switch] $IncludeGridData, [parameter(Mandatory = $false)] [ValidateSet("namedRanges","properties","sheets","spreadsheetId")] [string[]] $Fields, [parameter(Mandatory = $false)] [switch] $Raw ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/drive' ServiceType = 'Google.Apis.Sheets.v4.SheetsService' User = $User } $service = New-GoogleService @serviceParams } Process { try { if ($SheetName) { if ($Range -like "'*'!*") { throw "SpecifyRange formatting error! When using the SheetName parameter, please exclude the SheetName when formatting the SpecifyRange value (i.e. 'A1:Z1000')" } elseif ($Range) { $Range = "'$($SheetName)'!$Range" } else { $Range = "$SheetName" } } $request = $service.Spreadsheets.Get($SpreadsheetId) if ($Range) { $request.Ranges = [Google.Apis.Util.Repeatable[String]]::new([String[]]$Range) } if ($Fields) { $request.Fields = "$(($Fields | ForEach-Object {$f = $_;@("namedRanges","properties","sheets","spreadsheetId") | Where-Object {$_ -eq $f}}) -join ",")" } elseif ($PSBoundParameters.Keys -contains 'IncludeGridData') { $request.IncludeGridData = $IncludeGridData } else { $request.IncludeGridData = $true } Write-Verbose "Getting Spreadsheet Id '$SpreadsheetId' for user '$User'" $response = $request.Execute() if (!$Raw) { $response = $response | Select-Object @{N = "Title";E = {$_.properties.title}},@{N = "MaxRows";E = {[int]($_.sheets.properties.gridProperties.rowCount | Sort-Object | Select-Object -Last 1)}},@{N = "MaxColumns";E = {[int]($_.sheets.properties.gridProperties.columnCount | Sort-Object | Select-Object -Last 1)}},* } $response | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSSheetInfo' function Import-GSSheet { <# .SYNOPSIS Imports data from a Sheet as if it was a CSV .DESCRIPTION Imports data from a Sheet as if it was a CSV .PARAMETER SpreadsheetId The unique Id of the SpreadSheet to import data from .PARAMETER SheetName The name of the Sheet to import data from .PARAMETER User The owner of the SpreadSheet .PARAMETER Range The specific range to import data from .PARAMETER RowStart The starting row of data. Useful if the headers for your table are not in Row 1 of the Sheet .PARAMETER Headers Allows you to define the headers for the rows on the sheet, in case there is no header row .PARAMETER DateTimeRenderOption How to render the DateTime cells Available values are: * "FORMATTED_STRING" (Default) * "SERIAL_NUMBER" .PARAMETER ValueRenderOption How to render the value cells and formula cells Available values are: * "FORMATTED_VALUE" (Default) * "UNFORMATTED_VALUE" * "FORMULA" .PARAMETER MajorDimension The major dimension that results should use. For example, if the spreadsheet data is: A1=1,B1=2,A2=3,B2=4, then requesting range=A1:B2,majorDimension=ROWS will return [[1,2],[3,4]], whereas requesting range=A1:B2,majorDimension=COLUMNS will return [[1,3],[2,4]]. Available values are: * "ROWS" (Default) * "COLUMNS" * "DIMENSION_UNSPECIFIED" .PARAMETER As Whether to return the result set as an array of PSObjects or an array of DataRows Available values are: * "PSObject" (Default) * "DataRow" .PARAMETER Raw If $true, return the raw response, otherwise, return a flattened response for readability .EXAMPLE Import-GSSheet -SpreadsheetId '1rhsAYTOB_vrpvfwImPmWy0TcVa2sgmQa_9u976' -SheetName Sheet1 -RowStart 2 -Range 'B:C' Imports columns B-C as an Array of PSObjects, skipping the first row and treating Row 2 as the header row. Objects in the array will be what's contained in range 'B3:C' after that #> [cmdletbinding(DefaultParameterSetName = "Import")] Param ( [parameter(Mandatory = $true)] [String] $SpreadsheetId, [parameter(Mandatory = $false)] [String] $SheetName, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias('Owner','PrimaryEmail','UserKey','Mail')] [string] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [Alias('SpecifyRange')] [string] $Range, [parameter(Mandatory = $false,ParameterSetName = "Import")] [int] $RowStart = 1, [parameter(Mandatory = $false,ParameterSetName = "Import")] [string[]] $Headers, [parameter(Mandatory = $false)] [ValidateSet("FORMATTED_STRING","SERIAL_NUMBER")] [string] $DateTimeRenderOption = "FORMATTED_STRING", [parameter(Mandatory = $false)] [ValidateSet("FORMATTED_VALUE","UNFORMATTED_VALUE","FORMULA")] [string] $ValueRenderOption = "FORMATTED_VALUE", [parameter(Mandatory = $false)] [ValidateSet("ROWS","COLUMNS","DIMENSION_UNSPECIFIED")] [string] $MajorDimension = "ROWS", [Parameter(Mandatory = $false,ParameterSetName = "Import")] [ValidateSet("DataRow","PSObject")] [string] $As = "PSObject", [parameter(Mandatory = $false,ParameterSetName = "Raw")] [switch] $Raw ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/drive' ServiceType = 'Google.Apis.Sheets.v4.SheetsService' User = $User } $service = New-GoogleService @serviceParams } Process { try { if ($SheetName) { if ($Range -like "'*'!*") { throw "SpecifyRange formatting error! When using the SheetName parameter, please exclude the SheetName when formatting the SpecifyRange value (i.e. 'A1:Z1000')" } elseif ($Range) { $Range = "'$($SheetName)'!$Range" } else { $Range = "$SheetName" } } $request = $service.Spreadsheets.Values.BatchGet($SpreadsheetId) $request.Ranges = [Google.Apis.Util.Repeatable[String]]::new([String[]]$Range) $request.DateTimeRenderOption = [Google.Apis.Sheets.v4.SpreadsheetsResource+ValuesResource+GetRequest+DateTimeRenderOptionEnum]::$($DateTimeRenderOption -replace "_","") $request.ValueRenderOption = [Google.Apis.Sheets.v4.SpreadsheetsResource+ValuesResource+GetRequest+ValueRenderOptionEnum]::$($ValueRenderOption -replace "_","") $request.MajorDimension = [Google.Apis.Sheets.v4.SpreadsheetsResource+ValuesResource+GetRequest+MajorDimensionEnum]::$($MajorDimension -replace "_","") if ($MajorDimension -ne "ROWS" -and !$Raw) { $Raw = $true Write-Warning "Setting -Raw to True -- Parsing requires the MajorDimension to be set to ROWS (default value)" } Write-Verbose "Importing Range '$Range' from Spreadsheet '$SpreadsheetId' for user '$User'" $response = $request.Execute() if (!$Raw) { $i = 0 $datatable = New-Object System.Data.Datatable if ($Headers) { foreach ($col in $Headers) { [void]$datatable.Columns.Add("$col") } $i++ } $(if ($RowStart) { $response.valueRanges.values | Select-Object -Skip $([int]$RowStart - 1) } else { $response.valueRanges.values }) | ForEach-Object { if ($i -eq 0) { foreach ($col in $_) { [void]$datatable.Columns.Add("$col") } } else { [void]$datatable.Rows.Add([String[]]$_) } $i++ } switch ($As) { DataRow { Write-Verbose "Created DataTable with $($i - 1) DataRows" $datatable } PSObject { Write-Verbose "Created PSObject array with $($i - 1) objects" foreach ($row in $datatable) { $obj = [Ordered]@{} $props = $row.Table.Columns.ColumnName foreach ($prop in $props) { $obj[$prop] = $row.$prop } [PSCustomObject]$obj } } } } else { $response | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Import-GSSheet' function New-GSSheet { <# .SYNOPSIS Creates a new SpreadSheet .DESCRIPTION Creates a new SpreadSheet .PARAMETER Title The name of the new SpreadSheet .PARAMETER User The user to create the Sheet for .PARAMETER Launch If $true, opens the new SpreadSheet Url in your default browser .EXAMPLE New-GSSheet -Title "Finance Workbook" -Launch Creates a new SpreadSheet titled "Finance Workbook" and opens it in the browser on creation #> [cmdletbinding()] Param ( [parameter(Mandatory = $false)] [Alias('SheetTitle')] [String] $Title, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias('Owner','PrimaryEmail','UserKey','Mail')] [string] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false)] [Alias('Open')] [Switch] $Launch ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/drive' ServiceType = 'Google.Apis.Sheets.v4.SheetsService' User = $User } $service = New-GoogleService @serviceParams } Process { try { $body = New-Object 'Google.Apis.Sheets.v4.Data.Spreadsheet' $body.Properties = New-Object 'Google.Apis.Sheets.v4.Data.SpreadsheetProperties' -Property @{ Title = $Title } if (!$Title) { $Title = "Untitled spreadsheet" } Write-Verbose "Creating Spreadsheet '$Title' for user '$User'" $request = $service.Spreadsheets.Create($body) $response = $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru if ($Launch) { Write-Verbose "Launching new spreadsheet at $($response.SpreadsheetUrl)" Start-Process $response.SpreadsheetUrl } $response } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'New-GSSheet' function Clear-GSTasklist { <# .SYNOPSIS Clears all completed tasks from the specified task list. The affected tasks will be marked as 'hidden' and no longer be returned by default when retrieving all tasks for a task list .DESCRIPTION Clears all completed tasks from the specified task list. The affected tasks will be marked as 'hidden' and no longer be returned by default when retrieving all tasks for a task list .PARAMETER Tasklist The unique Id of the Tasklist to clear .PARAMETER User The User who owns the Tasklist. Defaults to the AdminUser's email. .EXAMPLE Clear-GSTasklist -Tasklist 'MTA3NjIwMjA1NTEzOTk0MjQ0OTk6NTMyNDY5NDk1NDM5MzMxO' -Confirm:$false Clears the specified Tasklist owned by the AdminEmail user and skips the confirmation check #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias('Id')] [String[]] $Tasklist, [parameter(Mandatory = $false,Position = 1)] [Alias("PrimaryEmail","UserKey","Mail","Email")] [ValidateNotNullOrEmpty()] [String] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/tasks' ServiceType = 'Google.Apis.Tasks.v1.TasksService' User = $User } $service = New-GoogleService @serviceParams } Process { foreach ($list in $Tasklist) { try { if ($PSCmdlet.ShouldProcess("Clearing Tasklist '$list' for user '$User'")) { $request = $service.Tasks.Clear($list) $request.Execute() Write-Verbose "Successfully cleared Tasklist '$list' for user '$User'" } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Clear-GSTasklist' function Get-GSTask { <# .SYNOPSIS Gets a specific Task or the list of Tasks .DESCRIPTION Gets a specific Task or the list of Tasks .PARAMETER User The User who owns the Task. Defaults to the AdminUser's email. .PARAMETER Task The unique Id of the Task. If left blank, returns the list of Tasks on the Tasklist .PARAMETER Tasklist The unique Id of the Tasklist the Task is on. .PARAMETER PageSize Page size of the result set .EXAMPLE Get-GSTasklist Gets the list of Tasklists owned by the AdminEmail user .EXAMPLE Get-GSTasklist -Tasklist MTUzNTU0MDYscM0NjKDMTIyNjQ6MDow -User john@domain.com Gets the Tasklist matching the provided Id owned by John #> [cmdletbinding(DefaultParameterSetName = "List")] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true,ParameterSetName = "Get")] [Alias('Id')] [String[]] $Task, [parameter(Mandatory = $true,Position = 1)] [String] $Tasklist, [parameter(Mandatory = $false)] [Alias("PrimaryEmail","UserKey","Mail","Email")] [ValidateNotNullOrEmpty()] [String] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false,ParameterSetName = "List")] [DateTime] $CompletedMax, [parameter(Mandatory = $false,ParameterSetName = "List")] [DateTime] $CompletedMin, [parameter(Mandatory = $false,ParameterSetName = "List")] [DateTime] $DueMax, [parameter(Mandatory = $false,ParameterSetName = "List")] [DateTime] $DueMin, [parameter(Mandatory = $false,ParameterSetName = "List")] [DateTime] $UpdatedMin, [parameter(Mandatory = $false,ParameterSetName = "List")] [Switch] $ShowCompleted, [parameter(Mandatory = $false,ParameterSetName = "List")] [Switch] $ShowDeleted, [parameter(Mandatory = $false,ParameterSetName = "List")] [Switch] $ShowHidden, [parameter(Mandatory = $false,ParameterSetName = "List")] [ValidateRange(1,100)] [Alias("MaxResults")] [Int] $PageSize ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/tasks.readonly' ServiceType = 'Google.Apis.Tasks.v1.TasksService' User = $User } $service = New-GoogleService @serviceParams } Process { switch ($PSCmdlet.ParameterSetName) { Get { foreach ($T in $Task) { try { Write-Verbose "Getting Task '$T' from Tasklist '$Tasklist' for user '$User'" $request = $service.Tasks.Get($Tasklist,$T) $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } List { try { Write-Verbose "Getting all Tasks from Tasklist '$Tasklist' for user '$User'" $request = $service.Tasks.List($Tasklist) foreach ($key in $PSBoundParameters.Keys | Where-Object {$request.PSObject.Properties.Name -contains $_}) { switch ($key) { Tasklist {} {$_ -in @('CompletedMax','CompletedMin','DueMax','DueMin','UpdatedMin')} { $request.$key = ($PSBoundParameters[$key]).ToString('o') } default { if ($request.PSObject.Properties.Name -contains $key) { $request.$key = $PSBoundParameters[$key] } } } } if ($PSBoundParameters.Keys -contains 'PageSize') { $request.MaxResults = $PSBoundParameters['PageSize'] } [int]$i = 1 do { $result = $request.Execute() $result.Items | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru $request.PageToken = $result.NextPageToken [int]$retrieved = ($i + $result.Items.Count) - 1 Write-Verbose "Retrieved $retrieved Tasks..." [int]$i = $i + $result.Items.Count } until (!$result.NextPageToken) } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } } Export-ModuleMember -Function 'Get-GSTask' function Get-GSTasklist { <# .SYNOPSIS Gets a specific Tasklist or the list of Tasklists .DESCRIPTION Gets a specific Tasklist or the list of Tasklists .PARAMETER User The User who owns the Tasklist. Defaults to the AdminUser's email. .PARAMETER Tasklist The unique Id of the Tasklist. If left blank, gets the full list of Tasklists .PARAMETER PageSize Page size of the result set .EXAMPLE Get-GSTasklist Gets the list of Tasklists owned by the AdminEmail user .EXAMPLE Get-GSTasklist -Tasklist MTUzNTU0MDYscM0NjKDMTIyNjQ6MDow -User john@domain.com Gets the Tasklist matching the provided Id owned by John #> [cmdletbinding(DefaultParameterSetName = "List")] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true,ParameterSetName = "Get")] [Alias('Id')] [String[]] $Tasklist, [parameter(Mandatory = $false,Position = 1)] [Alias("PrimaryEmail","UserKey","Mail","Email")] [ValidateNotNullOrEmpty()] [String] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false,ParameterSetName = "List")] [ValidateRange(1,100)] [Alias("MaxResults")] [Int] $PageSize ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/tasks.readonly' ServiceType = 'Google.Apis.Tasks.v1.TasksService' User = $User } $service = New-GoogleService @serviceParams } Process { switch ($PSCmdlet.ParameterSetName) { Get { foreach ($list in $Tasklist) { try { Write-Verbose "Getting Tasklist '$list' for user '$User'" $request = $service.Tasklists.Get($list) $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } List { try { Write-Verbose "Getting all Tasklists for user '$User'" $request = $service.Tasklists.List() if ($PSBoundParameters.Keys -contains 'PageSize') { $request.MaxResults = $PSBoundParameters['PageSize'] } [int]$i = 1 do { $result = $request.Execute() $result.Items | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru $request.PageToken = $result.NextPageToken [int]$retrieved = ($i + $result.Items.Count) - 1 Write-Verbose "Retrieved $retrieved Tasklists..." [int]$i = $i + $result.Items.Count } until (!$result.NextPageToken) } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } } Export-ModuleMember -Function 'Get-GSTasklist' function Move-GSTask { <# .SYNOPSIS Moves the specified task to another position in the task list. This can include putting it as a child task under a new parent and/or move it to a different position among its sibling tasks. .DESCRIPTION Moves the specified task to another position in the task list. This can include putting it as a child task under a new parent and/or move it to a different position among its sibling tasks. .PARAMETER Tasklist The unique Id of the Tasklist where the Task currently resides .PARAMETER Task The unique Id of the Task to move .PARAMETER Parent Parent task identifier. If the task is created at the top level, this parameter is omitted. .PARAMETER Previous Previous sibling task identifier. If the task is created at the first position among its siblings, this parameter is omitted. .PARAMETER User The User who owns the Tasklist. Defaults to the AdminUser's email. .EXAMPLE Clear-GSTasklist -Tasklist 'MTA3NjIwMjA1NTEzOTk0MjQ0OTk6NTMyNDY5NDk1NDM5MzMxO' -Confirm:$false Clears the specified Tasklist owned by the AdminEmail user and skips the confirmation check #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0)] [String] $Tasklist, [parameter(Mandatory = $true,Position = 1,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias('Id')] [String[]] $Task, [parameter(Mandatory = $false)] [String] $Parent, [parameter(Mandatory = $false)] [String] $Previous, [parameter(Mandatory = $false,Position = 1)] [Alias("PrimaryEmail","UserKey","Mail","Email")] [ValidateNotNullOrEmpty()] [String] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/tasks' ServiceType = 'Google.Apis.Tasks.v1.TasksService' User = $User } $service = New-GoogleService @serviceParams } Process { foreach ($T in $Task) { try { Write-Verbose "Moving Task '$T' for user '$User'" $request = $service.Tasks.Move($Tasklist,$T) foreach ($key in $PSBoundParameters.Keys | Where-Object {$request.PSObject.Properties.Name -contains $_}) { switch ($key) { {$_ -in @('Parent','Previous')} { $request.$key = $PSBoundParameters[$key] } } } $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Move-GSTask' function New-GSTask { <# .SYNOPSIS Creates a new Task .DESCRIPTION Creates a new Task .PARAMETER Title The title of the new Task .PARAMETER Tasklist The Id of the Tasklist to create the new Task on .PARAMETER Completed The DateTime of the task completion .PARAMETER Due The DateTime of the task due date .PARAMETER Notes Notes describing the task .PARAMETER Status Status of the task. This is either "needsAction" or "completed". .PARAMETER Parent Parent task identifier. If the task is created at the top level, this parameter is omitted. .PARAMETER Previous Previous sibling task identifier. If the task is created at the first position among its siblings, this parameter is omitted. .PARAMETER User The User to create the Task for. Defaults to the AdminUser's email. .EXAMPLE New-GSTask -Title 'Sweep kitchen','Mow lawn' -Tasklist MTA3NjIwMjA1NTEzOTk0MjQ0OTk6ODEzNTI1MjE3ODk0MTY2MDow Creates 2 new Tasks titled 'Sweep kitchen' and 'Mow lawn' for the AdminEmail user on the specified Tasklist Id #> [cmdletbinding()] Param ( [parameter(Mandatory = $true)] [String[]] $Title, [parameter(Mandatory = $true)] [String] $Tasklist, [parameter(Mandatory = $false)] [DateTime] $Completed, [parameter(Mandatory = $false)] [DateTime] $Due, [parameter(Mandatory = $false)] [String] $Notes, [parameter(Mandatory = $false)] [ValidateSet('needsAction','completed')] [String] $Status, [parameter(Mandatory = $false)] [String] $Parent, [parameter(Mandatory = $false)] [String] $Previous, [parameter(Mandatory = $false)] [Alias("PrimaryEmail","UserKey","Mail","Email")] [ValidateNotNullOrEmpty()] [String] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/tasks' ServiceType = 'Google.Apis.Tasks.v1.TasksService' User = $User } $service = New-GoogleService @serviceParams } Process { foreach ($T in $Title) { try { Write-Verbose "Creating Task '$T' on Tasklist '$Tasklist' for user '$User'" $body = New-Object 'Google.Apis.Tasks.v1.Data.Task' foreach ($key in $PSBoundParameters.Keys | Where-Object {$body.PSObject.Properties.Name -contains $_}) { switch ($key) { Parent {} default { if ($body.PSObject.Properties.Name -contains $key) { $body.$key = $PSBoundParameters[$key] } } } } $request = $service.Tasks.Insert($body,$Tasklist) foreach ($key in $PSBoundParameters.Keys | Where-Object {$request.PSObject.Properties.Name -contains $_}) { switch ($key) { Tasklist {} default { if ($request.PSObject.Properties.Name -contains $key) { $request.$key = $PSBoundParameters[$key] } } } } $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'New-GSTask' function New-GSTasklist { <# .SYNOPSIS Creates a new Tasklist .DESCRIPTION Creates a new Tasklist .PARAMETER User The User to create the Tasklist for. Defaults to the AdminUser's email. .PARAMETER Title The title of the new Tasklist .EXAMPLE New-GSTasklist -Title 'Chores','Projects' Creates 2 new Tasklists titled 'Chores' and 'Projects' for the AdminEmail user #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0)] [String[]] $Title, [parameter(Mandatory = $false,Position = 1)] [Alias("PrimaryEmail","UserKey","Mail","Email")] [ValidateNotNullOrEmpty()] [String] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/tasks' ServiceType = 'Google.Apis.Tasks.v1.TasksService' User = $User } $service = New-GoogleService @serviceParams } Process { foreach ($list in $Title) { try { Write-Verbose "Creating Tasklist '$list' for user '$User'" $body = New-Object 'Google.Apis.Tasks.v1.Data.TaskList' -Property @{ Title = $list } $request = $service.Tasklists.Insert($body) $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'New-GSTasklist' function Remove-GSTask { <# .SYNOPSIS Deletes the authenticated user's specified task .DESCRIPTION Deletes the authenticated user's specified task .PARAMETER Task The unique Id of the Task to delete .PARAMETER Tasklist The unique Id of the Tasklist where the Task is .PARAMETER User The User who owns the Tasklist. Defaults to the AdminUser's email. .EXAMPLE Remove-GSTask -Task 'MTA3NjIwMjA1NTEzOTk0MjQ0OTk6MDow' -Tasklist 'MTA3NjIwMjA1NTEzOTk0MjQ0OTk6NTMyNDY5NDk1NDM5MzMxO' -Confirm:$false Remove the specified Task owned by the AdminEmail user and skips the confirmation check #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias('Id')] [String[]] $Task, [parameter(Mandatory = $true,Position = 1)] [String] $Tasklist, [parameter(Mandatory = $false,Position = 1)] [Alias("PrimaryEmail","UserKey","Mail","Email")] [ValidateNotNullOrEmpty()] [String] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/tasks' ServiceType = 'Google.Apis.Tasks.v1.TasksService' User = $User } $service = New-GoogleService @serviceParams } Process { foreach ($T in $Task) { try { if ($PSCmdlet.ShouldProcess("Removing Task '$T' from Tasklist '$Tasklist' for user '$User'")) { $request = $service.Tasks.Delete($Tasklist,$T) $request.Execute() Write-Verbose "Successfully removed Task '$T' from Tasklist '$Tasklist' for user '$User'" } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Remove-GSTask' function Remove-GSTasklist { <# .SYNOPSIS Deletes the authenticated user's specified task list .DESCRIPTION Deletes the authenticated user's specified task list .PARAMETER Tasklist The unique Id of the Tasklist to remove .PARAMETER User The User who owns the Tasklist. Defaults to the AdminUser's email. .EXAMPLE Remove-GSTasklist -Tasklist 'MTA3NjIwMjA1NTEzOTk0MjQ0OTk6NTMyNDY5NDk1NDM5MzMxO' -Confirm:$false Remove the specified Tasklist owned by the AdminEmail user and skips the confirmation check #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias('Id')] [String[]] $Tasklist, [parameter(Mandatory = $false,Position = 1)] [Alias("PrimaryEmail","UserKey","Mail","Email")] [ValidateNotNullOrEmpty()] [String] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/tasks' ServiceType = 'Google.Apis.Tasks.v1.TasksService' User = $User } $service = New-GoogleService @serviceParams } Process { foreach ($list in $Tasklist) { try { if ($PSCmdlet.ShouldProcess("Removing Tasklist '$list' for user '$User'")) { $request = $service.Tasklists.Delete($list) $request.Execute() Write-Verbose "Successfully removed Tasklist '$list' for user '$User'" } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Remove-GSTasklist' function Update-GSTask { <# .SYNOPSIS Updates a Task .DESCRIPTION Updates a Task .PARAMETER Tasklist The Id of the Tasklist the Task is on .PARAMETER Task The Id of the Task .PARAMETER Title The title of the Task .PARAMETER Completed The DateTime of the task completion .PARAMETER Due The DateTime of the task due date .PARAMETER Notes Notes describing the task .PARAMETER Status Status of the task. This is either "needsAction" or "completed". .PARAMETER Parent Parent task identifier. If the task is created at the top level, this parameter is omitted. .PARAMETER Previous Previous sibling task identifier. If the task is created at the first position among its siblings, this parameter is omitted. .PARAMETER User The User who owns the Task Defaults to the AdminUser's email. .EXAMPLE Update-GSTask -Title 'Return Ben Crawford's call -Tasklist MTA3NjIwMjA1NTEzOTk0MjQ0OTk6ODEzNTI1MjE3ODk0MTY2MDow -Task 'MTA3NjIwMjA1NTEzOTk0MjQ0OTk6MDo4MjM4NDQ2MDA0MzIxMDEx' -Status completed Updates the specified Task's title and marks it as completed #> [cmdletbinding()] Param ( [parameter(Mandatory = $true)] [String] $Tasklist, [parameter(Mandatory = $true)] [String] $Task, [parameter(Mandatory = $false)] [String[]] $Title, [parameter(Mandatory = $false)] [DateTime] $Completed, [parameter(Mandatory = $false)] [DateTime] $Due, [parameter(Mandatory = $false)] [String] $Notes, [parameter(Mandatory = $false)] [ValidateSet('needsAction','completed')] [String] $Status, [parameter(Mandatory = $false)] [String] $Parent, [parameter(Mandatory = $false)] [String] $Previous, [parameter(Mandatory = $false)] [Alias("PrimaryEmail","UserKey","Mail","Email")] [ValidateNotNullOrEmpty()] [String] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/tasks' ServiceType = 'Google.Apis.Tasks.v1.TasksService' User = $User } $service = New-GoogleService @serviceParams } Process { try { Write-Verbose "Updating Task '$Task' on Tasklist '$Tasklist' for user '$User'" $body = New-Object 'Google.Apis.Tasks.v1.Data.Task' foreach ($key in $PSBoundParameters.Keys | Where-Object {$body.PSObject.Properties.Name -contains $_}) { switch ($key) { Parent {} default { if ($body.PSObject.Properties.Name -contains $key) { $body.$key = $PSBoundParameters[$key] } } } } $request = $service.Tasks.Update($body,$Tasklist,$Task) foreach ($key in $PSBoundParameters.Keys | Where-Object {$request.PSObject.Properties.Name -contains $_}) { switch ($key) { {$_ -in @('Tasklist','Task')} {} default { if ($request.PSObject.Properties.Name -contains $key) { $request.$key = $PSBoundParameters[$key] } } } } $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Update-GSTask' function Update-GSTasklist { <# .SYNOPSIS Updates a Tasklist title .DESCRIPTION Updates a Tasklist title .PARAMETER Tasklist The unique Id of the Tasklist to update .PARAMETER Title The new title of the Tasklist .PARAMETER User The User who owns the Tasklist. Defaults to the AdminUser's email. .EXAMPLE Update-GSTasklist -Tasklist 'MTA3NjIwMjA1NTEzOTk0MjQ0OTk6NTMyNDY5NDk1NDM5MzMxOTow' -Title 'Hi-Pri Callbacks' Updates the specified TaskList with the new title 'Hi-Pri Callbacks' #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0)] [String] $Tasklist, [parameter(Mandatory = $true,Position = 1)] [String] $Title, [parameter(Mandatory = $false)] [Alias("PrimaryEmail","UserKey","Mail","Email")] [ValidateNotNullOrEmpty()] [String] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/tasks' ServiceType = 'Google.Apis.Tasks.v1.TasksService' User = $User } $service = New-GoogleService @serviceParams } Process { try { Write-Verbose "Updating Tasklist '$list' to Title '$Title' for user '$User'" $body = New-Object 'Google.Apis.Tasks.v1.Data.TaskList' -Property @{ Title = $Title } $request = $service.Tasklists.Patch($body,$Tasklist) $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Update-GSTasklist' function Get-GSShortUrl { <# .SYNOPSIS Gets information about a user's Short Url's created at https://goo.gl/ .DESCRIPTION Gets information about a user's Short Url's created at https://goo.gl/ .PARAMETER ShortUrl The Short Url to return information for. If excluded, returns the list of the user's Short Url's .PARAMETER User The primary email of the user you would like to retrieve Short Url information for Defaults to the AdminEmail user .PARAMETER Projection Additional information to return. Acceptable values are: * "ANALYTICS_CLICKS" - Returns short URL click counts. * "FULL" - Returns short URL click counts. .EXAMPLE Get-GSShortUrl Gets the Short Url list of the AdminEmail user #> [cmdletbinding()] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias('Id')] [String[]] $ShortUrl, [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String[]] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory=$false)] [ValidateSet("Full","Analytics_Clicks")] [string] $Projection = "Full" ) Begin { if ($ShortUrl) { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($U)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/urlshortener' ServiceType = 'Google.Apis.Urlshortener.v1.UrlshortenerService' User = $User } $service = New-GoogleService @serviceParams } } Process { try { if ($ShortUrl) { foreach ($S in $ShortUrl) { Write-Verbose "Getting short Url '$S'" $request = $service.Url.Get($S) $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } } else { Get-GSShortUrlListPrivate @PSBoundParameters } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSShortUrl' function New-GSShortUrl { <# .SYNOPSIS Creates a new Short Url .DESCRIPTION Creates a new Short Url .PARAMETER LongUrl The full Url to shorten .PARAMETER User The user to create the Short Url for Defaults to the AdminEmail user .EXAMPLE New-GSShortUrl "http://ferrell.io" Creates a new Short Url pointing at http://ferrell.io/ #> [cmdletbinding()] Param ( [parameter(Mandatory = $true)] [String[]] $LongUrl, [parameter(Mandatory = $false,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String] $User = $Script:PSGSuite.AdminEmail ) Begin { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($U)@$($Script:PSGSuite.Domain)" } $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/urlshortener' ServiceType = 'Google.Apis.Urlshortener.v1.UrlshortenerService' User = $User } $service = New-GoogleService @serviceParams } Process { try { foreach ($L in $LongUrl) { Write-Verbose "Creating short Url for '$L'" $body = New-Object 'Google.Apis.Urlshortener.v1.Data.Url' -Property @{ LongUrl = $L } $request = $service.Url.Insert($body) $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'New-GSShortUrl' function Get-GSUser { <# .SYNOPSIS Gets the specified G SUite User or a list of Users .DESCRIPTION Gets the specified G SUite User. Designed for parity with Get-ADUser as much as possible .PARAMETER User The primary email or UserID of the user who you are trying to get info for. You can exclude the '@domain.com' to insert the Domain in the config or use the special 'me' to indicate the AdminEmail in the config. Defaults to the AdminEmail in the config .PARAMETER Filter Query string for searching user fields For more information on constructing user queries, see: https://developers.google.com/admin-sdk/directory/v1/guides/search-users PowerShell filter syntax here is supported as "best effort". Please use Google's filter operators and syntax to ensure best results .PARAMETER Domain The specific domain you would like to list users for. Useful for customers with multiple domains. .PARAMETER SearchBase The organizational unit path that you would like to list users from .PARAMETER SearchScope The depth at which to return the list of users Available values are: * "Base": only return the users specified in the SearchBase * "Subtree": return the full list of users underneath the specified SearchBase * "OneLevel": return the SearchBase and the Users directly underneath it .PARAMETER ShowDeleted Returns deleted users .PARAMETER Projection What subset of fields to fetch for this user Acceptable values are: * "Basic": Do not include any custom fields for the user * "Custom": Include custom fields from schemas requested in customFieldMask * "Full": Include all fields associated with this user (default for this module) .PARAMETER CustomFieldMask A comma-separated list of schema names. All fields from these schemas are fetched. This should only be set when using '-Projection Custom' .PARAMETER ViewType Whether to fetch the administrator-only or domain-wide public view of the user. For more information, see Retrieve a user as a non-administrator Acceptable values are: * "Admin_View": Results include both administrator-only and domain-public fields for the user. (default) * "Domain_Public": Results only include fields for the user that are publicly visible to other users in the domain. .PARAMETER Fields The specific fields to fetch for this user .PARAMETER PageSize Page size of the result set .PARAMETER OrderBy Property to use for sorting results. Acceptable values are: * "Email": Primary email of the user. * "FamilyName": User's family name. * "GivenName": User's given name. .PARAMETER SortOrder Whether to return results in ascending or descending order. Acceptable values are: * "Ascending": Ascending order. * "Descending": Descending order. .EXAMPLE Get-GSUser Gets the user info for the AdminEmail on the config .EXAMPLE Get-GSUser -Filter * Gets the list of users .EXAMPLE Get-GSUser -Filter "IsAdmin -eq '$true'" Gets the list of SuperAdmin users .EXAMPLE Get-GSUser -Filter "IsEnrolledIn2Sv -eq '$false'" -SearchBase /Contractors -SearchScope Subtree Gets the list of users not currently enrolled in 2-Step Verification from the Contractors OrgUnit or any OrgUnits underneath it #> [cmdletbinding(DefaultParameterSetName = "Get")] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true,ParameterSetName = "Get")] [Alias("PrimaryEmail","UserKey","Mail","Email","Id")] [ValidateNotNullOrEmpty()] [String[]] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false,ParameterSetName = "List")] [Alias("Query")] [String[]] $Filter, [parameter(Mandatory = $false,ParameterSetName = "List")] [String] $Domain, [parameter(Mandatory = $false,ParameterSetName = "List")] [Alias("OrgUnitPath")] [String] $SearchBase, [parameter(Mandatory = $false,ParameterSetName = "List")] [ValidateSet("Base","OneLevel","Subtree")] [String] $SearchScope = "Subtree", [parameter(Mandatory = $false,ParameterSetName = "List")] [Switch] $ShowDeleted, [parameter(Mandatory = $false)] [ValidateSet("Basic","Custom","Full")] [string] $Projection = "Full", [parameter(Mandatory = $false)] [String] $CustomFieldMask, [parameter(Mandatory = $false)] [ValidateSet("Admin_View","Domain_Public")] [String] $ViewType = "Admin_View", [parameter(Mandatory = $false)] [String[]] $Fields, [parameter(Mandatory = $false,ParameterSetName = "List")] [ValidateRange(1,500)] [Alias("MaxResults")] [Int] $PageSize = "500", [parameter(Mandatory = $false,ParameterSetName = "List")] [ValidateSet("Email","GivenName","FamilyName")] [String] $OrderBy, [parameter(Mandatory = $false,ParameterSetName = "List")] [ValidateSet("Ascending","Descending")] [String] $SortOrder ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.user.readonly' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { if ($MyInvocation.InvocationName -ne 'Get-GSUserList' -and $PSCmdlet.ParameterSetName -eq 'Get') { foreach ($U in $User) { try { if ( -not ($U -as [decimal])) { if ($U -ceq 'me') { $U = $Script:PSGSuite.AdminEmail } elseif ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } } Write-Verbose "Getting User '$U'" $request = $service.Users.Get($U) $request.Projection = $Projection $request.ViewType = ($ViewType -replace '_','') if ($CustomFieldMask) { $request.CustomFieldMask = $CustomFieldMask } if ($Fields) { $request.Fields = "$($Fields -join ",")" } $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $U -Force -PassThru | Add-Member -MemberType ScriptMethod -Name ToString -Value {$this.PrimaryEmail} -PassThru -Force } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } else { try { $request = $service.Users.List() $request.Projection = $Projection if ($PSBoundParameters.Keys -contains 'Domain') { $verbScope = "domain '$($PSBoundParameters['Domain'])'" $request.Domain = $PSBoundParameters['Domain'] } elseif ($Script:PSGSuite.Preference) { switch ($Script:PSGSuite.Preference) { Domain { $verbScope = "domain '$($Script:PSGSuite.Domain)'" $request.Domain = $Script:PSGSuite.Domain } CustomerID { $verbScope = "customer '$($Script:PSGSuite.CustomerID)'" $request.Customer = "$($Script:PSGSuite.CustomerID)" } } } else { $verbScope = "customer 'my_customer'" $request.Customer = "my_customer" } if ($PageSize) { $request.MaxResults = $PageSize } foreach ($prop in $PSBoundParameters.Keys | Where-Object {$_ -in @('OrderBy','SortOrder','CustomFieldMask','ShowDeleted','ViewType')}) { $request.$prop = $PSBoundParameters[$prop] } if (![String]::IsNullOrEmpty($Filter) -or $SearchBase) { if ($Filter -eq '*') { $Filter = "" } else { $Filter = "$($Filter -join " ")" } if ($SearchBase) { $Filter += " OrgUnitPath='$SearchBase'" } $Filter = $Filter -replace " -eq ","=" -replace " -like ",":" -replace " -match ",":" -replace " -contains ",":" -creplace "'True'","True" -creplace "'False'","False" $request.Query = $Filter.Trim() if ([String]::IsNullOrEmpty($Filter.Trim())) { Write-Verbose "Getting all Users for $verbScope" } else { Write-Verbose "Getting Users for $verbScope matching filter: `"$($Filter.Trim())`"" } } else { Write-Verbose "Getting all Users for $verbScope" } $response = New-Object System.Collections.ArrayList [int]$i = 1 do { $result = $request.Execute() if ($result.UsersValue) { $result.UsersValue | ForEach-Object { $_ | Add-Member -MemberType NoteProperty -Name 'User' -Value $_.PrimaryEmail -Force -PassThru | Add-Member -MemberType ScriptMethod -Name ToString -Value {$this.PrimaryEmail} -Force [void]$response.Add($_) } } $request.PageToken = $result.NextPageToken [int]$retrieved = ($i + $result.UsersValue.Count) - 1 Write-Verbose "Retrieved $retrieved users..." [int]$i = $i + $result.UsersValue.Count } until (!$result.NextPageToken) if ($SearchScope -ne "Subtree") { if (!$SearchBase) { $SearchBase = "/" } $response = switch ($SearchScope) { Base { $response | Where-Object {$_.OrgUnitPath -eq $SearchBase} } OneLevel { $maxDepth = ($SearchBase -split "/" | Where-Object {$_}).Count + 1 $children = $response | Select-Object -ExpandProperty OrgUnitPath -Unique | ForEach-Object { if (($_ -split "/" | Where-Object {$_}).Count -le $maxDepth) { $_ } } $response | Where-Object {$_.OrgUnitPath -in $children} } } Write-Verbose "Total users in SearchScope: $($response.Count)" } return $response } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Get-GSUser' function Get-GSUserAlias { <# .SYNOPSIS Gets the specified G SUite User's aliases .DESCRIPTION Gets the specified G SUite User's aliases .PARAMETER User The primary email or UserID of the user who you are trying to get aliases for. You can exclude the '@domain.com' to insert the Domain in the config or use the special 'me' to indicate the AdminEmail in the config. Defaults to the AdminEmail in the config .EXAMPLE Get-GSUserAlias Gets the list of aliases for the AdminEmail user #> [cmdletbinding()] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail","Email")] [ValidateNotNullOrEmpty()] [String[]] $User = $Script:PSGSuite.AdminEmail ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.user.readonly' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { foreach ($U in $User) { try { if ($U -ceq 'me') { $U = $Script:PSGSuite.AdminEmail } elseif ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } Write-Verbose "Getting Alias list for User '$U'" $request = $service.Users.Aliases.List($U) $request.Execute() | Select-Object -ExpandProperty AliasesValue } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Get-GSUserAlias' function Get-GSUserPhoto { <# .SYNOPSIS Gets the photo data for the specified user .DESCRIPTION Gets the photo data for the specified user .PARAMETER User The primary email or UserID of the user who you are trying to get info for. You can exclude the '@domain.com' to insert the Domain in the config or use the special 'me' to indicate the AdminEmail in the config. Defaults to the AdminEmail in the config .PARAMETER OutFilePath The directory path that you would like to save the photos to. If excluded, this will return the photo information .PARAMETER OutFileFormat The format that you would like to save the photo as. Available values are: * "PNG": saves the photo in .png format * "JPG": saves the photo in .jpg format * "Base64": saves the photo as a .txt file containing standard (non-WebSafe) Base64 content. Defaults to PNG .EXAMPLE Get-GSUserPhoto -OutFilePath . Saves the Google user photo of the AdminEmail in the current working directory as a .png image #> [cmdletbinding()] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true,ParameterSetName = "Get")] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String[]] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false)] [ValidateScript({(Get-Item $_).PSIsContainer})] [String] $OutFilePath, [parameter(Mandatory = $false)] [ValidateSet('Base64','PNG','JPG')] [String] $OutFileFormat = 'PNG' ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.user.readonly' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { try { foreach ($U in $User) { if ($U -ceq 'me') { $U = $Script:PSGSuite.AdminEmail } elseif ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } Write-Verbose "Getting photo for User '$U'" $request = $service.Users.Photos.Get($U) $res = $request.Execute() $base64 = $res.PhotoData | Convert-Base64 -From WebSafeBase64String -To Base64String $bytes = [Convert]::FromBase64String($base64) if ($OutFilePath) { $fileBaseName = "$($U -replace '@.*','')" switch ($OutFileFormat) { JPG { $filePath = Join-Path $OutFilePath "$($fileBaseName).jpg" Write-Verbose "Saving photo at '$filePath'" [System.IO.File]::WriteAllBytes($filePath, $bytes) } PNG { $filePath = Join-Path $OutFilePath "$($fileBaseName).png" Write-Verbose "Saving photo at '$filePath'" [System.IO.File]::WriteAllBytes($filePath, $bytes) } Base64 { $filePath = Join-Path $OutFilePath "$($fileBaseName).txt" Write-Verbose "Saving Base64 photo content at '$filePath'" [System.IO.File]::WriteAllText($filePath,$base64) } } } else { $res | Add-Member -MemberType NoteProperty -Name 'User' -Value $U -PassThru | Add-Member -MemberType NoteProperty -Name 'PhotoBytes' -Value $bytes -PassThru | Add-Member -MemberType NoteProperty -Name 'PhotoBase64' -Value $base64 -PassThru } } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Get-GSUserPhoto' function New-GSUser { <# .SYNOPSIS Creates a new G Suite user .DESCRIPTION Creates a new G Suite user .PARAMETER PrimaryEmail The primary email for the user. If a user with the desired email already exists, a GoogleApiException will be thrown .PARAMETER GivenName The given (first) name of the user .PARAMETER FamilyName The family (last) name of the user .PARAMETER FullName The full name of the user, if different from "$FirstName $LastName" .PARAMETER Password The password for the user. Requires a SecureString .PARAMETER ChangePasswordAtNextLogin If set, user will need to change their password on their first login .PARAMETER OrgUnitPath The OrgUnitPath to create the user in .PARAMETER Suspended If set, user will be created in a suspended state .PARAMETER Addresses The address objects of the user This parameter expects a 'Google.Apis.Admin.Directory.directory_v1.Data.UserAddress[]' object type. You can create objects of this type easily by using the function 'Add-GSUserAddress' .PARAMETER Emails The email objects of the user This parameter expects a 'Google.Apis.Admin.Directory.directory_v1.Data.UserEmail[]' object type. You can create objects of this type easily by using the function 'Add-GSUserEmail' .PARAMETER ExternalIds The externalId objects of the user This parameter expects a 'Google.Apis.Admin.Directory.directory_v1.Data.UserExternalId[]' object type. You can create objects of this type easily by using the function 'Add-GSUserExternalId' .PARAMETER Organizations The organization objects of the user This parameter expects a 'Google.Apis.Admin.Directory.directory_v1.Data.UserOrganization[]' object type. You can create objects of this type easily by using the function 'Add-GSUserOrganization' .PARAMETER Relations The relation objects of the user This parameter expects a 'Google.Apis.Admin.Directory.directory_v1.Data.UserRelation[]' object type. You can create objects of this type easily by using the function 'Add-GSUserRelation' .PARAMETER Phones The phone objects of the user This parameter expects a 'Google.Apis.Admin.Directory.directory_v1.Data.UserPhone[]' object type. You can create objects of this type easily by using the function 'Add-GSUserPhone' .PARAMETER IncludeInGlobalAddressList Indicates if the user's profile is visible in the G Suite global address list when the contact sharing feature is enabled for the domain. For more information about excluding user profiles, see the administration help center: http://support.google.com/a/bin/answer.py?answer=1285988 .PARAMETER IpWhitelisted If true, the user's IP address is white listed: http://support.google.com/a/bin/answer.py?answer=60752 .PARAMETER CustomSchemas Custom user attribute values to add to the user's account. The Custom Schema and it's fields **MUST** exist prior to updating these values for a user otherwise it will return an error. This parameter only accepts a hashtable where the keys are Schema Names and the value for each key is another hashtable, i.e.: Update-GSUser -User john.smith@domain.com -CustomSchemas @{ schemaName1 = @{ fieldName1 = $fieldValue1 fieldName2 = $fieldValue2 } schemaName2 = @{ fieldName3 = $fieldValue3 } } If you need to CLEAR a custom schema value, simply pass $null as the value(s) for the fieldName in the hashtable, i.e.: Update-GSUser -User john.smith@domain.com -CustomSchemas @{ schemaName1 = @{ fieldName1 = $null fieldName2 = $null } schemaName2 = @{ fieldName3 = $null } } .EXAMPLE $address = Add-GSUserAddress -Country USA -Locality Dallas -PostalCode 75000 Region TX -StreetAddress '123 South St' -Type Work -Primary $phone = Add-GSUserPhone -Type Work -Value "(800) 873-0923" -Primary $extId = Add-GSUserExternalId -Type Login_Id -Value jsmith2 $email = Add-GSUserEmail -Type work -Address jsmith@contoso.com New-GSUser -PrimaryEmail john.smith@domain.com -GivenName John -FamilyName Smith -Password (ConvertTo-SecureString -String 'Password123' -AsPlainText -Force) -ChangePasswordAtNextLogin -OrgUnitPath "/Users/New Hires" -IncludeInGlobalAddressList -Addresses $address -Phones $phone -ExternalIds $extId -Emails $email Creates a user named John Smith and adds their work address, work phone, login_id and alternate non gsuite work email to the user object. #> [cmdletbinding()] Param ( [parameter(Mandatory = $true, Position = 0)] [String] $PrimaryEmail, [parameter(Mandatory = $true)] [String] $GivenName, [parameter(Mandatory = $true)] [String] $FamilyName, [parameter(Mandatory = $false)] [String] $FullName, [parameter(Mandatory = $true)] [SecureString] $Password, [parameter(Mandatory = $false)] [Switch] $ChangePasswordAtNextLogin, [parameter(Mandatory = $false)] [String] $OrgUnitPath, [parameter(Mandatory = $false)] [Switch] $Suspended, [parameter(Mandatory = $false)] [Google.Apis.Admin.Directory.directory_v1.Data.UserAddress[]] $Addresses, [parameter(Mandatory = $false)] [Google.Apis.Admin.Directory.directory_v1.Data.UserEmail[]] $Emails, [parameter(Mandatory = $false)] [Google.Apis.Admin.Directory.directory_v1.Data.UserExternalId[]] $ExternalIds, [parameter(Mandatory = $false)] [Google.Apis.Admin.Directory.directory_v1.Data.UserOrganization[]] $Organizations, [parameter(Mandatory = $false)] [Google.Apis.Admin.Directory.directory_v1.Data.UserRelation[]] $Relations, [parameter(Mandatory = $false)] [Google.Apis.Admin.Directory.directory_v1.Data.UserPhone[]] $Phones, [parameter(Mandatory = $false)] [Switch] $IncludeInGlobalAddressList, [parameter(Mandatory = $false)] [Switch] $IpWhitelisted, [parameter(Mandatory = $false)] [ValidateScript( { $hash = $_ foreach ($schemaName in $hash.Keys) { if ($hash[$schemaName].GetType().Name -ne 'Hashtable') { throw "The CustomSchemas parameter only accepts a hashtable where the value of the top-level keys must also be a hashtable. The key '$schemaName' has a value of type '$($hash[$schemaName].GetType().Name)'" $valid = $false } else { $valid = $true } } $valid })] [Hashtable] $CustomSchemas ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.user' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { try { Write-Verbose "Creating user '$PrimaryEmail'" $body = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.User' $name = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.UserName' -Property @{ GivenName = $GivenName FamilyName = $FamilyName } if ($PSBoundParameters.Keys -contains 'FullName') { $name.FullName = $FullName } else { $name.FullName = "$GivenName $FamilyName" } $body.Name = $name foreach ($prop in $PSBoundParameters.Keys | Where-Object {$body.PSObject.Properties.Name -contains $_}) { switch ($prop) { PrimaryEmail { if ($PSBoundParameters[$prop] -notlike "*@*.*") { $PSBoundParameters[$prop] = "$($PSBoundParameters[$prop])@$($Script:PSGSuite.Domain)" } $body.$prop = $PSBoundParameters[$prop] } Password { $body.Password = (New-Object PSCredential "user", $Password).GetNetworkCredential().Password } Emails { $emailList = New-Object 'System.Collections.Generic.List`1[Google.Apis.Admin.Directory.directory_v1.Data.UserEmail]' foreach ($email in $Emails) { $emailList.Add($email) } $body.Emails = $emailList } ExternalIds { $extIdList = New-Object 'System.Collections.Generic.List`1[Google.Apis.Admin.Directory.directory_v1.Data.UserExternalId]' foreach ($extId in $ExternalIds) { $extIdList.Add($extId) } $body.ExternalIds = $extIdList } Organizations { $orgList = New-Object 'System.Collections.Generic.List`1[Google.Apis.Admin.Directory.directory_v1.Data.UserOrganization]' foreach ($organization in $Organizations) { $orgList.Add($organization) } $body.Organizations = $orgList } Relations { $relList = New-Object 'System.Collections.Generic.List`1[Google.Apis.Admin.Directory.directory_v1.Data.UserRelation]' foreach ($relation in $Relations) { $relList.Add($relation) } $body.Relations = $relList } Phones { $phoneList = New-Object 'System.Collections.Generic.List`1[Google.Apis.Admin.Directory.directory_v1.Data.UserPhone]' foreach ($phone in $Phones) { $phoneList.Add($phone) } $body.Phones = $phoneList } CustomSchemas { $schemaDict = New-Object 'System.Collections.Generic.Dictionary`2[[System.String],[System.Collections.Generic.IDictionary`2[[System.String],[System.Object]]]]' foreach ($schemaName in $CustomSchemas.Keys) { $fieldDict = New-Object 'System.Collections.Generic.Dictionary`2[[System.String],[System.Object]]' $schemaFields = $CustomSchemas[$schemaName] $schemaFields.Keys | ForEach-Object { $fieldDict.Add($_, $schemaFields[$_]) } $schemaDict.Add($schemaName, $fieldDict) } $body.CustomSchemas = $schemaDict } Default { $body.$prop = $PSBoundParameters[$prop] } } } $request = $service.Users.Insert($body) $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'New-GSUser' function New-GSUserAlias { <# .SYNOPSIS Creates a new alias for a G Suite user .DESCRIPTION Creates a new alias for a G Suite user .PARAMETER User The user to create the alias for .PARAMETER Alias The alias or list of aliases to create for the user .EXAMPLE New-GSUserAlias -User john.smith@domain.com -Alias 'jsmith@domain.com','johns@domain.com' Creates 2 new aliases for user John Smith as 'jsmith@domain.com' and 'johns@domain.com' #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail","Email")] [ValidateNotNullOrEmpty()] [String] $User, [parameter(Mandatory = $true,Position = 1)] [String[]] $Alias ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.user' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { foreach ($A in $Alias) { try { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } if ($A -notlike "*@*.*") { $A = "$($A)@$($Script:PSGSuite.Domain)" } Write-Verbose "Creating alias '$A' for user '$User'" $body = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.Alias' $body.AliasValue = $A $request = $service.Users.Aliases.Insert($body,$User) $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'New-GSUserAlias' function Remove-GSUser { <# .SYNOPSIS Removes a user .DESCRIPTION Removes a user .PARAMETER User The primary email or unique Id of the user to Remove-GSUser .EXAMPLE Remove-GSUser joe -Confirm:$false Removes the user 'joe@domain.com', skipping confirmation #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $false,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String[]] $User ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.user' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { foreach ($U in $User) { try { if ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } if ($PSCmdlet.ShouldProcess("Deleting user '$U'")) { Write-Verbose "Deleting user '$U'" $request = $service.Users.Delete($U) $request.Execute() Write-Verbose "User '$U' has been successfully deleted" } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Remove-GSUser' function Remove-GSUserAlias { <# .SYNOPSIS Removes an alias from a G Suite user .DESCRIPTION Removes an alias from a G Suite user .PARAMETER User The user to remove the alias from .PARAMETER Alias The alias or list of aliases to remove from the user .EXAMPLE Remove-GSUserAlias -User john.smith@domain.com -Alias 'jsmith@domain.com','johns@domain.com' Removes 2 aliases from user John Smith: 'jsmith@domain.com' and 'johns@domain.com' #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail","Email")] [ValidateNotNullOrEmpty()] [String] $User, [parameter(Mandatory = $true,Position = 1)] [String[]] $Alias ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.user' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { foreach ($A in $Alias) { try { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } if ($A -notlike "*@*.*") { $A = "$($A)@$($Script:PSGSuite.Domain)" } if ($PSCmdlet.ShouldProcess("Removing alias '$A' from user '$User'")) { Write-Verbose "Removing alias '$A' from user '$User'" $request = $service.Users.Aliases.Delete($User,$A) $request.Execute() Write-Verbose "Alias '$A' has been successfully deleted from user '$User'" } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Remove-GSUserAlias' function Remove-GSUserPhoto { <# .SYNOPSIS Removes the photo for the specified user .DESCRIPTION Removes the photo for the specified user .PARAMETER User The primary email or UserID of the user who you are trying to remove the photo for. You can exclude the '@domain.com' to insert the Domain in the config or use the special 'me' to indicate the AdminEmail in the config. .EXAMPLE Remove-GSUserPhoto -User me Removes the Google user photo of the AdminEmail user #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String[]] $User ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.user' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { foreach ($U in $User) { try { if ($U -ceq 'me') { $U = $Script:PSGSuite.AdminEmail } elseif ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } if ($PSCmdlet.ShouldProcess("Removing the photo for User '$U'")) { Write-Verbose "Removing the photo for User '$U'" $request = $service.Users.Photos.Delete($U) $request.Execute() Write-Verbose "Successfully removed the photo for user '$U'" } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Remove-GSUserPhoto' function Restore-GSUser { <# .SYNOPSIS Restores a deleted user .DESCRIPTION Restores a deleted user .PARAMETER User The email address of the user to restore .PARAMETER Id The unique Id of the user to restore .PARAMETER OrgUnitPath The OrgUnitPath to restore the user to Defaults to the root OrgUnit "/" .PARAMETER RecentOnly If multiple users with the email address are found in deleted users, this forces restoration of the most recently deleted user. If not passed and multiple deleted users are found with the specified email address, you will be prompted to choose which you'd like to restore based on deletion time .EXAMPLE Restore-GSUser -User john.smith@domain.com -OrgUnitPath "/Users/Rehires" -Confirm:$false Restores user John Smith to the OrgUnitPath "/Users/Rehires", skipping confirmation. If multiple accounts with the email "john.smith@domain.com" are found, the user is presented with a dialog to choose which account to restore based on deletion time #> [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "Medium",DefaultParameterSetName = 'User')] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true,ParameterSetName = 'User')] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String[]] $User, [parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true,ParameterSetName = 'Id')] [Int] $Id, [parameter(Mandatory = $false,Position = 1)] [String] $OrgUnitPath = "/", [parameter(Mandatory = $false)] [Switch] $RecentOnly ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.user' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams if (!$User) { $User = ($Id | ForEach-Object {"$_"}) $GetId = $false } } Process { try { foreach ($U in $User) { $body = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.UserUndelete' $body.OrgUnitPath = $OrgUnitPath if (!$Id) { if ($PSCmdlet.ShouldProcess("Undeleting user '$U'")) { if ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } $delUsers = Get-GSUser -Filter "email=$U" -ShowDeleted -Verbose:$false | Where-Object {$_.PrimaryEmail -eq $U} | Sort-Object DeletionTime -Descending $userId = if ($delUsers.Count -gt 1 -and !$RecentOnly) { $i = 0 $options = @() $idHash = @{} $delUsers | ForEach-Object { $i++ $optText = "$($i): $($_.DeletionTime.ToString())" $options += @{"&$($optText)" = "User '$($_.PrimaryEmail)' deleted on $($_.DeletionTime.ToLongDateString()) at $($_.DeletionTime.ToLongTimeString())"} $idHash[$optText] = $_.Id } $choice = Read-Prompt -Options $options -Title "`n** Choose Which User To Undelete **`n" -Message "There are $($delUsers.Count) deleted users with the email address '$U'. Please enter the number for the user you would like to undelete based on the time the account was deleted`n" $idHash[$choice] } else { $delUsers[0].Id } } } else { $userId = $U } Write-Verbose "Undeleting User Id '$userId' [$U]" $request = $service.Users.Undelete($body,$userId) $request.Execute() Write-Verbose "User '$U' has been successfully undeleted" } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Restore-GSUser' function Sync-GSUserCache { <# .SYNOPSIS Syncs your GS Users to a hashtable contained in the global scoped variable $global:GSUserCache for fast lookups in scripts. .DESCRIPTION Syncs your GS Users to a hashtable contained in the global scoped variable $global:GSUserCache for fast lookups in scripts. .PARAMETER Filter The filter to use with Get-GSUser to populate your UserCache with. Defaults to * (all users). If you'd like to limit to just Active (not suspended) users, use the following filter: "IsSuspended -eq '$false'" .PARAMETER Keys The user properties to use as keys in the Cache hash. Available values are: * PrimaryEmail * Id * Alias Defaults to all 3. .PARAMETER PassThru If $true, returns the hashtable as output .EXAMPLE Sync-GSUserCache -Filter 'IsSuspended=False' Fills the $global:GSUserCache hashtable with all active users using the default Keys. #> [CmdLetBinding()] Param ( [parameter(Mandatory = $false, Position = 0)] [String[]] $Filter = @('*'), [parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [ValidateSet('PrimaryEmail','Id','Alias')] [String[]] $Keys = @('PrimaryEmail','Id','Alias'), [parameter(Mandatory = $false)] [Switch] $PassThru ) Begin { $global:GSUserCache = @{} } Process { Write-Verbose "Syncing users to `$global:GSUserCache" Get-GSUser -Filter $Filter | ForEach-Object { if ($Keys -contains 'Id') { $global:GSUserCache[$_.Id] = $_ } if ($Keys -contains 'PrimaryEmail') { $global:GSUserCache[$_.PrimaryEmail] = $_ } if ($Keys -contains 'Alias') { foreach ($email in $_.Emails.Address) { if (-not ($global:GSUserCache.ContainsKey($email))) { $global:GSUserCache[$email] = $_ } } } } } End { if ($PassThru) { return $global:GSUserCache } } } Export-ModuleMember -Function 'Sync-GSUserCache' function Update-GSUser { <# .SYNOPSIS Updates a user .DESCRIPTION Updates a user .PARAMETER User The primary email or unique Id of the user to update .PARAMETER PrimaryEmail The new primary email for the user. The previous primary email will become an alias automatically .PARAMETER GivenName The new given (first) name for the user .PARAMETER FamilyName The new family (last) name for the user .PARAMETER FullName The new full name for the user .PARAMETER Password The new password for the user as a SecureString .PARAMETER ChangePasswordAtNextLogin If set, user will need to change their password on their next login .PARAMETER OrgUnitPath The new OrgUnitPath for the user .PARAMETER Suspended If set to $true or passed as a bare switch (-Suspended), user will be suspended. If set to $false, user will be unsuspended. If excluded, user's suspension status will remain as-is .PARAMETER Addresses The address objects of the user This parameter expects a 'Google.Apis.Admin.Directory.directory_v1.Data.UserAddress[]' object type. You can create objects of this type easily by using the function 'Add-GSUserAddress' .PARAMETER Emails The email objects of the user This parameter expects a 'Google.Apis.Admin.Directory.directory_v1.Data.UserEmail[]' object type. You can create objects of this type easily by using the function 'Add-GSUserEmail' .PARAMETER ExternalIds The externalId objects of the user This parameter expects a 'Google.Apis.Admin.Directory.directory_v1.Data.UserExternalId[]' object type. You can create objects of this type easily by using the function 'Add-GSUserExternalId' To CLEAR all values for a user, pass `$null` as the value for this parameter. .PARAMETER Organizations The organization objects of the user This parameter expects a 'Google.Apis.Admin.Directory.directory_v1.Data.UserOrganization[]' object type. You can create objects of this type easily by using the function 'Add-GSUserOrganization' To CLEAR all values for a user, pass `$null` as the value for this parameter. .PARAMETER Relations A list of the user's relationships to other users. This parameter expects a 'Google.Apis.Admin.Directory.directory_v1.Data.UserRelation[]' object type. You can create objects of this type easily by using the function 'Add-GSUserRelation' To CLEAR all values for a user, pass `$null` as the value for this parameter. .PARAMETER Phones The phone objects of the user This parameter expects a 'Google.Apis.Admin.Directory.directory_v1.Data.UserPhone[]' object type. You can create objects of this type easily by using the function 'Add-GSUserPhone' To CLEAR all values for a user, pass `$null` as the value for this parameter. .PARAMETER IncludeInGlobalAddressList Indicates if the user's profile is visible in the G Suite global address list when the contact sharing feature is enabled for the domain. For more information about excluding user profiles, see the administration help center: http://support.google.com/a/bin/answer.py?answer=1285988 .PARAMETER IpWhitelisted If true, the user's IP address is white listed: http://support.google.com/a/bin/answer.py?answer=60752 .PARAMETER IsAdmin If true, the user will be made a SuperAdmin. If $false, the user will have SuperAdmin privileges revoked. Requires confirmation. .PARAMETER CustomSchemas Custom user attribute values to add to the user's account. This parameter only accepts a hashtable where the keys are Schema Names and the value for each key is another hashtable, i.e.: Update-GSUser -User john.smith@domain.com -CustomSchemas @{ schemaName1 = @{ fieldName1 = $fieldValue1 fieldName2 = $fieldValue2 } schemaName2 = @{ fieldName3 = $fieldValue3 } } If you need to CLEAR a custom schema value, simply pass $null as the value(s) for the fieldName in the hashtable, i.e.: Update-GSUser -User john.smith@domain.com -CustomSchemas @{ schemaName1 = @{ fieldName1 = $null fieldName2 = $null } schemaName2 = @{ fieldName3 = $null } } The Custom Schema and it's fields **MUST** exist prior to updating these values for a user otherwise it will return an error. .EXAMPLE Update-GSUser -User john.smith@domain.com -PrimaryEmail johnathan.smith@domain.com -GivenName Johnathan -Suspended:$false Updates user john.smith@domain.com with a new primary email of "johnathan.smith@domain.com", sets their Given Name to "Johnathan" and unsuspends them. Their previous primary email "john.smith@domain.com" will become an alias on their account automatically #> [cmdletbinding(SupportsShouldProcess = $true, ConfirmImpact = "High")] Param ( [parameter(Mandatory = $true, Position = 0, ValueFromPipelineByPropertyName = $true)] [Alias("Id", "UserKey", "Mail")] [ValidateNotNullOrEmpty()] [String[]] $User, [parameter(Mandatory = $false)] [String] $PrimaryEmail, [parameter(Mandatory = $false)] [String] $GivenName, [parameter(Mandatory = $false)] [String] $FamilyName, [parameter(Mandatory = $false)] [String] $FullName, [parameter(Mandatory = $false)] [SecureString] $Password, [parameter(Mandatory = $false)] [Switch] $ChangePasswordAtNextLogin, [parameter(Mandatory = $false)] [String] $OrgUnitPath, [parameter(Mandatory = $false)] [Switch] $Suspended, [parameter(Mandatory = $false)] [Google.Apis.Admin.Directory.directory_v1.Data.UserAddress[]] $Addresses, [parameter(Mandatory = $false)] [Google.Apis.Admin.Directory.directory_v1.Data.UserEmail[]] $Emails, [parameter(Mandatory = $false)] [Google.Apis.Admin.Directory.directory_v1.Data.UserExternalId[]] $ExternalIds, [parameter(Mandatory = $false)] [Google.Apis.Admin.Directory.directory_v1.Data.UserOrganization[]] $Organizations, [parameter(Mandatory = $false)] [Google.Apis.Admin.Directory.directory_v1.Data.UserRelation[]] $Relations, [parameter(Mandatory = $false)] [Google.Apis.Admin.Directory.directory_v1.Data.UserPhone[]] $Phones, [parameter(Mandatory = $false)] [Switch] $IncludeInGlobalAddressList, [parameter(Mandatory = $false)] [Switch] $IpWhitelisted, [parameter(Mandatory = $false)] [Switch] $IsAdmin, [parameter(Mandatory = $false)] [ValidateScript( { $hash = $_ foreach ($schemaName in $hash.Keys) { if ($hash[$schemaName].GetType().Name -ne 'Hashtable') { throw "The CustomSchemas parameter only accepts a hashtable where the value of the top-level keys must also be a hashtable. The key '$schemaName' has a value of type '$($hash[$schemaName].GetType().Name)'" $valid = $false } else { $valid = $true } } $valid })] [Hashtable] $CustomSchemas ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.user' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams } Process { foreach ($U in $User) { try { if ($U -ceq 'me') { $U = $Script:PSGSuite.AdminEmail } elseif ($U -notlike "*@*.*") { $U = "$($U)@$($Script:PSGSuite.Domain)" } Write-Verbose "Updating user '$U'" $userObj = Get-GSUser $U -Verbose:$false $body = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.User' $name = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.UserName' $nameUpdated = $false $toClear = @{} foreach ($prop in $PSBoundParameters.Keys | Where-Object {$body.PSObject.Properties.Name -contains $_ -or $name.PSObject.Properties.Name -contains $_}) { switch ($prop) { PrimaryEmail { if ($PSBoundParameters[$prop] -notlike "*@*.*") { $PSBoundParameters[$prop] = "$($PSBoundParameters[$prop])@$($Script:PSGSuite.Domain)" } $body.$prop = $PSBoundParameters[$prop] } GivenName { $name.$prop = $PSBoundParameters[$prop] $nameUpdated = $true } FamilyName { $name.$prop = $PSBoundParameters[$prop] $nameUpdated = $true } FullName { $name.$prop = $PSBoundParameters[$prop] $nameUpdated = $true } Password { $body.Password = (New-Object PSCredential "user", $Password).GetNetworkCredential().Password } CustomSchemas { $schemaDict = New-Object 'System.Collections.Generic.Dictionary`2[[System.String],[System.Collections.Generic.IDictionary`2[[System.String],[System.Object]]]]' foreach ($schemaName in $CustomSchemas.Keys) { $fieldDict = New-Object 'System.Collections.Generic.Dictionary`2[[System.String],[System.Object]]' $schemaFields = $CustomSchemas[$schemaName] $schemaFields.Keys | ForEach-Object { $fieldDict.Add($_, $schemaFields[$_]) } $schemaDict.Add($schemaName, $fieldDict) } $body.CustomSchemas = $schemaDict } Emails { $emailList = New-Object 'System.Collections.Generic.List`1[Google.Apis.Admin.Directory.directory_v1.Data.UserEmail]' foreach ($email in $Emails) { $emailList.Add($email) } $body.Emails = $emailList } ExternalIds { if ($null -ne $ExternalIds) { $extIdList = New-Object 'System.Collections.Generic.List`1[Google.Apis.Admin.Directory.directory_v1.Data.UserExternalId]' foreach ($extId in $ExternalIds) { $extIdList.Add($extId) } $body.ExternalIds = $extIdList } else { $toClear['externalIds'] = $null } } Organizations { if ($null -ne $Organizations) { $orgList = New-Object 'System.Collections.Generic.List`1[Google.Apis.Admin.Directory.directory_v1.Data.UserOrganization]' foreach ($organization in $Organizations) { $orgList.Add($organization) } $body.Organizations = $orgList } else { $toClear['organizations'] = $null } } Relations { if ($null -ne $Relations) { $relList = New-Object 'System.Collections.Generic.List`1[Google.Apis.Admin.Directory.directory_v1.Data.UserRelation]' foreach ($relation in $Relations) { $relList.Add($relation) } $body.Relations = $relList } else { $toClear['relations'] = $null } } Phones { if ($null -ne $Relations) { $phoneList = New-Object 'System.Collections.Generic.List`1[Google.Apis.Admin.Directory.directory_v1.Data.UserPhone]' foreach ($phone in $Phones) { $phoneList.Add($phone) } $body.Phones = $phoneList } else { $toClear['phones'] = $null } } IsAdmin { if ($userObj.IsAdmin -eq $PSBoundParameters[$prop]) { Write-Verbose "User '$U' already has IsAdmin set to '$($userObj.IsAdmin)'" } else { if ($PSCmdlet.ShouldProcess("Updating user '$U' to IsAdmin '$($PSBoundParameters[$prop])'")) { Write-Verbose "Updating user '$U' to IsAdmin '$($PSBoundParameters[$prop])'" $adminBody = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.UserMakeAdmin' -Property @{ Status = $PSBoundParameters[$prop] } $request = $service.Users.MakeAdmin($adminBody, $userObj.Id) $request.Execute() } } } Default { $body.$prop = $PSBoundParameters[$prop] } } } if ($nameUpdated) { $body.Name = $name } $request = $service.Users.Update($body, $userObj.Id) $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $U -PassThru if ($toClear.Keys.Count) { $header = @{ Authorization = "Bearer $(Get-GSToken -Scopes "https://www.googleapis.com/auth/admin.directory.user" -Verbose:$false)" } $uri = [Uri]"https://www.googleapis.com/admin/directory/v1/users/$U" Write-Verbose "Clearing out all values for User '$U' on the following properties: [ $($toClear.Keys -join ", ") ]" $null = Invoke-RestMethod -Method Put -Uri $uri -Headers $header -Body $($toClear | ConvertTo-Json -Depth 5 -Compress) -ContentType 'application/json' -Verbose:$false -ErrorAction Stop } } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } } Export-ModuleMember -Function 'Update-GSUser' function Update-GSUserPhoto { <# .SYNOPSIS Updates the photo for the specified user .DESCRIPTION Updates the photo for the specified user .PARAMETER User The primary email or UserID of the user who you are trying to update the photo for. You can exclude the '@domain.com' to insert the Domain in the config or use the special 'me' to indicate the AdminEmail in the config. .PARAMETER Path The path of the photo that you would like to update the user with. .EXAMPLE Update-GSUserPhoto -User me -Path .\myphoto.png Updates the Google user photo of the AdminEmail with the image at the specified path #> [cmdletbinding()] Param ( [parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [ValidateNotNullOrEmpty()] [String] $User, [parameter(Mandatory = $true,Position = 1)] [ValidateScript({Test-Path $_})] [String] $Path ) Begin { $serviceParams = @{ Scope = 'https://www.googleapis.com/auth/admin.directory.user' ServiceType = 'Google.Apis.Admin.Directory.directory_v1.DirectoryService' } $service = New-GoogleService @serviceParams $Path = (Resolve-Path $Path).Path } Process { try { if ($User -ceq 'me') { $User = $Script:PSGSuite.AdminEmail } elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } $mimeType = Get-MimeType -File $Path $photoData = [System.Convert]::ToBase64String((Get-Content $Path -Encoding Byte)) | Convert-Base64 -From Base64String -To WebSafeBase64String $body = New-Object 'Google.Apis.Admin.Directory.directory_v1.Data.UserPhoto' -Property @{ PhotoData = $photoData MimeType = $mimeType } Write-Verbose "Updating the photo for User '$User' with file '$Path'" $request = $service.Users.Photos.Update($body,$User) $request.Execute() } catch { if ($ErrorActionPreference -eq 'Stop') { $PSCmdlet.ThrowTerminatingError($_) } else { Write-Error $_ } } } } Export-ModuleMember -Function 'Update-GSUserPhoto' Import-GoogleSDK if ($global:PSGSuiteKey -and $MyInvocation.BoundParameters['Debug']) { $prevDebugPref = $DebugPreference $DebugPreference = "Continue" Write-Debug "`$global:PSGSuiteKey is set to a $($global:PSGSuiteKey.Count * 8)-bit key!" $DebugPreference = $prevDebugPref } $aliasHash = @{ 'Add-GSDriveFilePermissions' = 'Add-GSDrivePermission' 'Export-PSGSuiteConfiguration' = 'Set-PSGSuiteConfig' 'Get-GSCalendarResourceList' = 'Get-GSResourceList' 'Get-GSCalendarEventList' = 'Get-GSCalendarEvent' 'Get-GSDataTransferApplicationList' = 'Get-GSDataTransferApplication' 'Get-GSDriveFileInfo' = 'Get-GSDriveFile' 'Get-GSDriveFilePermissionsList' = 'Get-GSDrivePermission' 'Get-GSGmailDelegates' = 'Get-GSGmailDelegate' 'Get-GSGmailFilterList' = 'Get-GSGmailFilter' 'Get-GSGmailLabelList' = 'Get-GSGmailLabel' 'Get-GSGmailMessageInfo' = 'Get-GSGmailMessage' 'Get-GSGroupList' = 'Get-GSGroup' 'Get-GSGroupMemberList' = 'Get-GSGroupMember' 'Get-GSMobileDeviceList' = 'Get-GSMobileDevice' 'Get-GSOrganizationalUnitList' = 'Get-GSOrganizationalUnit' 'Get-GSOrgUnit' = 'Get-GSOrganizationalUnit' 'Get-GSOrgUnitList' = 'Get-GSOrganizationalUnit' 'Get-GSOU' = 'Get-GSOrganizationalUnit' 'Get-GSResourceList' = 'Get-GSResource' 'Get-GSShortURLInfo' = 'Get-GSShortURL' 'Get-GSTeamDrivesList' = 'Get-GSTeamDrive' 'Get-GSUserASPList' = 'Get-GSUserASP' 'Get-GSUserLicenseInfo' = 'Get-GSUserLicense' 'Get-GSUserLicenseList' = 'Get-GSUserLicense' 'Get-GSUserList' = 'Get-GSUser' 'Get-GSUserSchemaInfo' = 'Get-GSUserSchema' 'Get-GSUserSchemaList' = 'Get-GSUserSchema' 'Get-GSUserTokenList' = 'Get-GSUserToken' 'Import-PSGSuiteConfiguration' = 'Get-PSGSuiteConfig' 'Move-GSGmailMessageToTrash' = 'Remove-GSGmailMessage' 'New-GSCalendarResource' = 'New-GSResource' 'Remove-GSGmailMessageFromTrash' = 'Restore-GSGmailMessage' 'Set-PSGSuiteDefaultDomain' = 'Switch-PSGSuiteConfig' 'Switch-PSGSuiteDomain' = 'Switch-PSGSuiteConfig' 'Update-GSCalendarResource' = 'Update-GSResource' 'Update-GSSheetValue' = 'Export-GSSheet' } foreach ($key in $aliasHash.Keys) { try { New-Alias -Name $key -Value $aliasHash[$key] -Force } catch { Write-Error "[ALIAS: $($key)] $($_.Exception.Message.ToString())" } } Export-ModuleMember -Alias '*' if (!(Test-Path (Join-Path "~" ".scrthq"))) { New-Item -Path (Join-Path "~" ".scrthq") -ItemType Directory -Force | Out-Null } if ($PSVersionTable.ContainsKey('PSEdition') -and $PSVersionTable.PSEdition -eq 'Core' -and !$Global:PSGSuiteKey -and !$IsWindows) { if (!(Test-Path (Join-Path (Join-Path "~" ".scrthq") "BlockCoreCLREncryptionWarning.txt"))) { Write-Warning "CoreCLR does not support DPAPI encryption! Setting a basic AES key to prevent errors. Please create a unique key as soon as possible as this will only obfuscate secrets from plain text in the Configuration, the key is not secure as is. If you would like to prevent this message from displaying in the future, run the following command: Block-CoreCLREncryptionWarning " } $Global:PSGSuiteKey = [Byte[]]@(1..16) $ConfigScope = "User" } if ($Global:PSGSuiteKey -is [System.Security.SecureString]) { $Method = "SecureString" if (!$ConfigScope) { $ConfigScope = "Machine" } } elseif ($Global:PSGSuiteKey -is [System.Byte[]]) { $Method = "AES Key" if (!$ConfigScope) { $ConfigScope = "Machine" } } else { $Method = "DPAPI" $ConfigScope = "User" } Add-MetadataConverter -Converters @{ [SecureString] = { $encParams = @{} if ($Global:PSGSuiteKey -is [System.Byte[]]) { $encParams["Key"] = $Global:PSGSuiteKey } elseif ($Global:PSGSuiteKey -is [System.Security.SecureString]) { $encParams["SecureKey"] = $Global:PSGSuiteKey } 'Secure "{0}"' -f (ConvertFrom-SecureString $_ @encParams) } "Secure" = { param([string]$String) $encParams = @{} if ($Global:PSGSuiteKey -is [System.Byte[]]) { $encParams["Key"] = $Global:PSGSuiteKey } elseif ($Global:PSGSuiteKey -is [System.Security.SecureString]) { $encParams["SecureKey"] = $Global:PSGSuiteKey } ConvertTo-SecureString $String @encParams } } try { $confParams = @{ Scope = $ConfigScope } if ($ConfigName) { $confParams["ConfigName"] = $ConfigName $Script:ConfigName = $ConfigName } try { Get-PSGSuiteConfig @confParams -ErrorAction Stop } catch { if (Test-Path "$ModuleRoot\$env:USERNAME-$env:COMPUTERNAME-$env:PSGSuiteDefaultDomain-PSGSuite.xml") { Get-PSGSuiteConfig -Path "$ModuleRoot\$env:USERNAME-$env:COMPUTERNAME-$env:PSGSuiteDefaultDomain-PSGSuite.xml" -ErrorAction Stop Write-Warning "No Configuration.psd1 found at scope '$ConfigScope'; falling back to legacy XML. If you would like to convert your legacy XML to the newer Configuration.psd1, run the following command: Get-PSGSuiteConfig -Path '$ModuleRoot\$env:USERNAME-$env:COMPUTERNAME-$env:PSGSuiteDefaultDomain-PSGSuite.xml' -PassThru | Set-PSGSuiteConfig " } else { Write-Warning "There was no config returned! Please make sure you are using the correct key or have a configuration already saved." } } } catch { Write-Warning "There was no config returned! Please make sure you are using the correct key or have a configuration already saved." } |