forked from kenlasko/BackupRestoreTeamsEV
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRestore-TeamsEV.ps1
More file actions
executable file
·254 lines (211 loc) · 9.15 KB
/
Restore-TeamsEV.ps1
File metadata and controls
executable file
·254 lines (211 loc) · 9.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
<#
.SYNOPSIS
A script to automatically restore a backed-up Teams Enterprise Voice configuration.
.DESCRIPTION
A script to automatically restore a backed-up Teams Enterprise Voice configuration. Requires a backup run using Backup-TeamsEV.ps1 in the same directory as the script. Will restore the following items:
- Dialplans and associated normalization rules
- Voice routes
- Voice routing policies
- PSTN usages
- Outbound translation rules
.PARAMETER File
REQUIRED. Path to the zip file containing the backed up Teams EV config to restore
.PARAMETER KeepExisting
OPTIONAL. Will not erase existing Enterprise Voice configuration before restoring.
.PARAMETER OverrideAdminDomain
OPTIONAL: The FQDN your Office365 tenant. Use if your admin account is not in the same domain as your tenant (ie. doesn't use a @tenantname.onmicrosoft.com address)
.NOTES
Version 1.10
Build: Feb 04, 2020
Copyright © 2020 Ken Lasko
klasko@ucdialplans.com
https://www.ucdialplans.com
#>
[CmdletBinding(ConfirmImpact = 'Medium',
SupportsShouldProcess)]
param
(
[Parameter(Mandatory, HelpMessage = 'Path to the zip file containing the backed up Teams EV config to restore',
ValueFromPipelineByPropertyName)]
[string]
$File,
[switch]
$KeepExisting,
[string]
$OverrideAdminDomain
)
Try {
$ZipPath = (Resolve-Path -Path $File)
$null = (Add-Type -AssemblyName System.IO.Compression.FileSystem)
$ZipStream = [io.compression.zipfile]::OpenRead($ZipPath)
}
Catch {
Write-Error -Message 'Could not open zip archive.' -ErrorAction Stop
Exit
}
If ((Get-PSSession | Where-Object -FilterScript {
$_.Computername -match "online.lync.com" -or $_.ComputerName -eq "api.interfaces.records.teams.microsoft.com"
}).State -eq 'Opened') {
Write-Host -Object 'Using existing session credentials'
}
Else {
Write-Host -Object 'Logging into Office 365...'
If ($OverrideAdminDomain) {
$O365Session = (New-CsOnlineSession -OverrideAdminDomain $OverrideAdminDomain)
}
Else {
$O365Session = (New-CsOnlineSession)
}
$null = (Import-PSSession -Session $O365Session -AllowClobber)
}
$EV_Entities = 'Dialplans', 'VoiceRoutes', 'VoiceRoutingPolicies', 'PSTNUsages', 'TranslationRules', 'PSTNGateways'
Write-Host -Object 'Validating backup files.'
ForEach ($EV_Entity in $EV_Entities) {
Try {
$ZipItem = $ZipStream.GetEntry("$EV_Entity.txt")
$ItemReader = (New-Object -TypeName System.IO.StreamReader -ArgumentList ($ZipItem.Open()))
$null = (Set-Variable -Name $EV_Entity -Value ($ItemReader.ReadToEnd() | ConvertFrom-Json))
# Throw error if there is no Identity field, which indicates this isn't a proper backup file
If ($null -eq ((Get-Variable -Name $EV_Entity).Value[0].Identity)) {
$null = (Set-Variable -Name $EV_Entity -Value $NULL)
Throw ('Error')
}
}
Catch {
Write-Error -Message ($EV_Entity + '.txt could not be found, was empty or could not be parsed. ' + $EV_Entity + ' will not be restored.') -ErrorAction Continue
}
$ItemReader.Close()
}
If (!$KeepExisting) {
$Confirm = Read-Host -Prompt 'WARNING: This will ERASE all existing dialplans/voice routes/policies etc prior to restoring from backup. Continue (Y/N)?'
If ($Confirm -notmatch '^[Yy]$') {
Write-Host -Object 'Exiting without making changes.'
Exit
}
Write-Host -Object 'Erasing all existing dialplans/voice routes/policies etc.'
Write-Verbose 'Erasing all tenant dialplans'
$null = (Get-CsTenantDialPlan -ErrorAction SilentlyContinue | Remove-CsTenantDialPlan -ErrorAction SilentlyContinue)
Write-Verbose 'Erasing all online voice routes'
$null = (Get-CsOnlineVoiceRoute -ErrorAction SilentlyContinue | Remove-CsOnlineVoiceRoute -ErrorAction SilentlyContinue)
Write-Verbose 'Erasing all online voice routing policies'
$null = (Get-CsOnlineVoiceRoutingPolicy -ErrorAction SilentlyContinue | Remove-CsOnlineVoiceRoutingPolicy -ErrorAction SilentlyContinue)
Write-Verbose 'Erasing all PSTN usages'
$null = (Set-CsOnlinePstnUsage -Identity Global -Usage $NULL -ErrorAction SilentlyContinue)
Write-Verbose 'Removing translation rules from PSTN gateways'
$null = (Get-CsOnlinePSTNGateway -ErrorAction SilentlyContinue | Set-CsOnlinePSTNGateway -OutboundTeamsNumberTranslationRules $NULL -OutboundPstnNumberTranslationRules $NULL -ErrorAction SilentlyContinue)
Write-Verbose 'Removing translation rules'
$null = (Get-CsTeamsTranslationRule -ErrorAction SilentlyContinue | Remove-CsTeamsTranslationRule -ErrorAction SilentlyContinue)
}
# Rebuild tenant dialplans from backup
Write-Host -Object 'Restoring tenant dialplans'
ForEach ($Dialplan in $Dialplans) {
Write-Verbose "Restoring $($Dialplan.Identity) dialplan"
$DPExists = (Get-CsTenantDialPlan -Identity $Dialplan.Identity -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Identity)
$DPDetails = @{
Identity = $Dialplan.Identity
OptimizeDeviceDialing = $Dialplan.OptimizeDeviceDialing
Description = $Dialplan.Description
}
# Only include the external access prefix if one is defined. MS throws an error if you pass a null/empty ExternalAccessPrefix
If ($Dialplan.ExternalAccessPrefix) {
$DPDetails.Add("ExternalAccessPrefix", $Dialplan.ExternalAccessPrefix)
}
If ($DPExists) {
$null = (Set-CsTenantDialPlan @DPDetails)
}
Else {
$null = (New-CsTenantDialPlan @DPDetails)
}
# Create a new Object
$NormRules = @()
ForEach ($NormRule in $Dialplan.NormalizationRules) {
$NRDetails = @{
Parent = $Dialplan.Identity
Name = [regex]::Match($NormRule, '(?ms)Name=(.*?);').Groups[1].Value
Pattern = [regex]::Match($NormRule, '(?ms)Pattern=(.*?);').Groups[1].Value
Translation = [regex]::Match($NormRule, '(?ms)Translation=(.*?);').Groups[1].Value
Description = [regex]::Match($NormRule, '(?ms)^Description=(.*?);').Groups[1].Value
IsInternalExtension = [Convert]::ToBoolean([regex]::Match($NormRule, '(?ms)IsInternalExtension=(.*?)$').Groups[1].Value)
}
$NormRules += New-CsVoiceNormalizationRule @NRDetails -InMemory
}
$null = (Set-CsTenantDialPlan -Identity $Dialplan.Identity -NormalizationRules $NormRules)
}
# Rebuild PSTN usages from backup
Write-Host -Object 'Restoring PSTN usages'
ForEach ($PSTNUsage in $PSTNUsages.Usage) {
Write-Verbose "Restoring $PSTNUsage PSTN usage"
$null = (Set-CsOnlinePstnUsage -Identity Global -Usage @{Add = $PSTNUsage} -WarningAction SilentlyContinue -ErrorAction SilentlyContinue)
}
# Rebuild voice routes from backup
Write-Host -Object 'Restoring voice routes'
ForEach ($VoiceRoute in $VoiceRoutes) {
Write-Verbose "Restoring $($VoiceRoute.Identity) voice route"
$VRExists = (Get-CsOnlineVoiceRoute -Identity $VoiceRoute.Identity -ErrorAction SilentlyContinue).Identity
$VRDetails = @{
Identity = $VoiceRoute.Identity
NumberPattern = $VoiceRoute.NumberPattern
Priority = $VoiceRoute.Priority
OnlinePstnUsages = $VoiceRoute.OnlinePstnUsages
OnlinePstnGatewayList = $VoiceRoute.OnlinePstnGatewayList
Description = $VoiceRoute.Description
}
If ($VRExists) {
$null = (Set-CsOnlineVoiceRoute @VRDetails)
}
Else {
$null = (New-CsOnlineVoiceRoute @VRDetails)
}
}
# Rebuild voice routing policies from backup
Write-Host -Object 'Restoring voice routing policies'
ForEach ($VoiceRoutingPolicy in $VoiceRoutingPolicies) {
Write-Verbose "Restoring $($VoiceRoutingPolicy.Identity) voice routing policy"
$VPExists = (Get-CsOnlineVoiceRoutingPolicy -Identity $VoiceRoutingPolicy.Identity -ErrorAction SilentlyContinue).Identity
$VPDetails = @{
Identity = $VoiceRoutingPolicy.Identity
OnlinePstnUsages = $VoiceRoutingPolicy.OnlinePstnUsages
Description = $VoiceRoutingPolicy.Description
}
If ($VPExists) {
$null = (Set-CsOnlineVoiceRoutingPolicy @VPDetails)
}
Else {
$null = (New-CsOnlineVoiceRoutingPolicy @VPDetails)
}
}
# Rebuild outbound translation rules from backup
Write-Host -Object 'Restoring outbound translation rules'
ForEach ($TranslationRule in $TranslationRules) {
Write-Verbose "Restoring $($TranslationRule.Identity) translation rule"
$TRExists = (Get-CsTeamsTranslationRule -Identity $TranslationRule.Identity -ErrorAction SilentlyContinue).Identity
$TRDetails = @{
Identity = $TranslationRule.Identity
Pattern = $TranslationRule.Pattern
Translation = $TranslationRule.Translation
Description = $TranslationRule.Description
}
If ($TRExists) {
$null = (Set-CsTeamsTranslationRule @TRDetails)
}
Else {
$null = (New-CsTeamsTranslationRule @TRDetails)
}
}
# Re-add translation rules to PSTN gateways
Write-Host -Object 'Re-adding translation rules to PSTN gateways'
ForEach ($PSTNGateway in $PSTNGateways) {
Write-Verbose "Restoring translation rules to $($PSTNGateway.Identity)"
$GWExists = (Get-CsOnlinePSTNGateway -Identity $PSTNGateway.Identity -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Identity)
$GWDetails = @{
Identity = $PSTNGateway.Identity
OutbundTeamsNumberTranslationRules = $PSTNGateway.OutbundTeamsNumberTranslationRules #Sadly Outbund isn't a spelling mistake here. That's what the command uses.
OutboundPstnNumberTranslationRules = $PSTNGateway.OutboundPstnNumberTranslationRules
InboundTeamsNumberTranslationRules = $PSTNGateway.InboundTeamsNumberTranslationRules
InboundPstnNumberTranslationRules = $PSTNGateway.InboundPstnNumberTranslationRules
}
If ($GWExists) {
$null = (Set-CsOnlinePSTNGateway @GWDetails)
}
}
Write-Host -Object 'Finished!'