Public/Support/VoiceConfig/Grant-TeamsEmergencyAddress.ps1

#SupportsConfirm: MID
# Module: Orbit.Teams
# Function: VoiceConfig
# Author: David Eberhardt
# Updated: 18-MAY-2021
# Status: RC

#FIX Use-Case is gone for Direct Routing - Location would be assigned to the number, not the user.

function Grant-TeamsEmergencyAddress {
  <#
  .SYNOPSIS
    Grants an existing Emergency Address (CivicAddress) to a User
  .DESCRIPTION
    The Civic Address used as an Emergency Address is assigned to the CsOnlineVoiceUser Object
    This is done by Name (Description) of the Address instead of the Id
  .PARAMETER Identity
    Required. UserPrincipalName or ObjectId of the User Object or a TelephoneNumber
  .PARAMETER Address
    Required. Friendly name of the Address as specified in the Tenant or LocationId of the Address.
    LocationIds are taken as-is, friendly names are queried against Get-CsOnlineLisLocation for a defined Location
  .PARAMETER PassThru
    Optional. Displays Object after action.
  .EXAMPLE
    Grant-TeamsEmergencyAddress -Identity John@domain.com -Address "3rd Floor Cafe"
 
    Searches for the Civic Address with the Exact description of "3rd Floor Cafe" and assigns this Address to the User
  .EXAMPLE
    Grant-TeamsEmergencyAddress -Identity +15551234567 -Address "3rd Floor Cafe"
 
    Searches for the Civic Address with the Exact description of "3rd Floor Cafe" and
    assigns this Address to the Number +15551234567 if found in the Business Voice Directory
    AddressDescription is an Alias for Address
  .EXAMPLE
    Grant-TeamsEmergencyAddress -Identity John@domain.com -LocationId 0000000-0000-000000000000
 
    Searches for the Civic Address with the LocationId 0000000-0000-000000000000 and assigns this Address to the User
    LocationId is an Alias for Address
  .EXAMPLE
    Grant-TeamsEmergencyAddress -Identity +15551234567 -PolicyName 0000000-0000-000000000000
 
    Searches for the Civic Address with the LocationId 0000000-0000-000000000000 and
    assigns this Address to the Number +15551234567 if found in the Business Voice Directory
    PolicyName is an Alias for Address (as it fits the theme)
  .INPUTS
    System.String
  .OUTPUTS
    System.Void
  .NOTES
    This script looks up the Civic Address in the Lis-Database and feeds the Address Object to Set-CsOnlineVoiceUser
    This treats the Address like a Policy and behaves in the same way as the EmergencyCallingPolicy or the
    EmergencyCallRoutingPolicy to assign to a user. Accepts the Address Description or a LocationId directly.
    Can be utilised like any other policy. Aliases to Address are: AddressDescription, LocationId, PolicyName.
    https://docs.microsoft.com/en-us/microsoftteams/manage-emergency-call-routing-policies
    https://docs.microsoft.com/en-us/microsoftteams/configure-dynamic-emergency-calling
  .COMPONENT
    VoiceConfig
  .FUNCTIONALITY
    Changes the CsOnlineVoiceUser Object to add a Civic Address to the User or Phone Number
  .LINK
    https://github.com/DEberhardt/Orbit/tree/main/docs/Orbit.Teams/Grant-TeamsEmergencyAddress.md
  .LINK
    https://github.com/DEberhardt/Orbit/tree/main/docs/about/about_VoiceConfiguration.md
  .LINK
    https://github.com/DEberhardt/Orbit/tree/main/docs/
  #>


  [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')]
  [Alias('Grant-TeamsEA')]
  [OutputType([System.Void])]
  param(
    [Parameter(Mandatory, Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName, HelpMessage = 'Identity of the CsOnlineVoiceUser or TelephoneNumber')]
    [Alias('UserPrincipalName', 'ObjectId', 'PhoneNumber')]
    [string]$Identity,

    [Parameter(Mandatory, Position = 1, ValueFromPipelineByPropertyName, HelpMessage = 'Type of Object presented. Determines Output')]
    [Alias('Address', 'AddressDescription', 'LocationId')]
    [string]$PolicyName,

    [Parameter(HelpMessage = 'No output is written by default, Switch PassThru will return changed object')]
    [switch]$PassThru
  ) #param

  begin {
    Show-OrbitFunctionStatus -Level RC
    Write-Verbose -Message "[BEGIN ] $($MyInvocation.MyCommand)"

    # Asserting MicrosoftTeams Connection
    if ( -not (Assert-MicrosoftTeamsConnection) ) { throw 'Connection to Microsoft Teams not established. Please validate connection' }

    # preparing Splatting Object
    $Parameters = @{}
    $Parameters.ErrorAction = 'Stop'

    try {
      $CsOnlineLisLocation = if ( $script:OrbitRegexGuid.isMatch($PolicyName) ) {
        Get-CsOnlineLisLocation -LocationId $PolicyName -ErrorAction Stop
      }
      else {
        Get-CsOnlineLisLocation -Location "$PolicyName" -ErrorAction Stop
      }

      $Parameters.LocationId = $CsOnlineLisLocation.LocationId
    }
    catch {
      throw "Location '$PolicyName' not found (CsOnlineLisLocation)! Please provide LocationId or Address Description"
    }

  }
  process {
    Write-Verbose -Message "[PROCESS] $($MyInvocation.MyCommand)"

    foreach ($Id in $Identity) {
      Write-Verbose -Message "[PROCESS] Processing '$Id'"

      # Determining type of Id
      if ( $script:OrbitRegexPhoneNumber.isMatch($Id) ) {
        $Number = Format-StringForUse $Id -As E164
        Write-Verbose -Message "Identity matches a Phone Number - Number normalised to '$Number'"
        $Parameters.PhoneNumber = $Number
      }
      else {
        # Querying Identity
        try {
          Write-Verbose -Message "User '$User' - Querying User Account"
          $CsUser = Get-CsOnlineUser -Identity "$User" -WarningAction SilentlyContinue -ErrorAction Stop
        }
        catch {
          Write-Error -Message "User '$User' not found (CsOnlineUser): $($_.Exception.Message)" -Category ObjectNotFound
          continue
        }
        $Parameters.Identity = $CsUser.Identity
      }

      # Validating additional requirement for Set-CsPhoneNumberAssignment
      if ( $null -eq $Parameters.PhoneNumber ) {
        if ( $null -eq $CsUser.LineUri ) {
          Write-Error -Message "User '$User' has no PhoneNumber assigned. Either assign number first or use Set-CsPhoneNumberAssignment to assign both number and Location" -Category ObjectNotFound
          continue
        }
        else {
          try {
            $TeamsPhoneNumberObject = Get-CsPhoneNumberAssignment -TelephoneNumber $($CsUser.LineUri | Format-StringForUse -As Number) -ErrorAction Stop
            if ( $TeamsPhoneNumberObject.NumberType -eq 'DirectRouting' ) {
              throw 'Address Assignment not possible for DirectRouting numbers'
            }
            else {
              $Parameters.PhoneNumber = $TeamsPhoneNumberObject.TelephoneNumber
              $Parameters.PhoneNumberType = $TeamsPhoneNumberObject.NumberType
            }
          }
          catch {
            Write-Error -Message "User '$User' - Error determining PhoneNumberAssignment: $($_.Exception.Message)"
            continue
          }
        }
      }

      # Apply
      try {
        if ($PSCmdlet.ShouldProcess("$Identity", 'Set-CsPhoneNumberAssignment')) {
          #TEST This no longer works. Replacement with Set-CsPhoneNumberAssignment but that also requires a Phone Number to be applied! Add To Set-TeamsUVC?
          #Static, no splatting, only for Identities (not for phone numbers!)
          #$CsOnlineVoiceUser = Set-CsOnlineVoiceUser -Identity "$Identity" -LocationID $CsOnlineLisLocation.LocationId
          #$CsOnlineVoiceUser = $null
          Set-CsPhoneNumberAssignment @Parameters
          # Output
          if ( $PassThru ) {
            $CsOnlineVoiceUser = Get-CsPhoneNumberAssignment -TelephoneNumber $($CsUser.LineUri | Format-StringForUse -As Number) -ErrorAction Stop
            Write-Output $CsOnlineVoiceUser
          }
        }
      }
      catch {
        throw "Error applying Location to CsOnlineVoiceUser. Exception: $($_.Exception.Message)"
      }

      #TODO
    }
  } #process

  end {
    Write-Verbose -Message "[END ] $($MyInvocation.MyCommand)"

  } #end
} # Grant-TeamsEmergencyAddress