-
Notifications
You must be signed in to change notification settings - Fork 11
/
Invoke-D365FFOAxDBRestoreFromBACPAC.ps1
288 lines (242 loc) · 12.7 KB
/
Invoke-D365FFOAxDBRestoreFromBACPAC.ps1
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
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
## ***** Download bacpac file *********
#Save it to c:\temp\ or d:\temp\ for Azure VM
#If you have a bacpac file locally, then $BacpacSasLinkFromLCS should be empty, and $f should have a value, i.e., $f = Get-ChildItem D:\temp\CFBSSalesbackup.bacpac
#Use the following PowerShell script to convert the bacpac file to the SQL Database
#https://github.com/valerymoskalenko/D365FFO-PowerShell-scripts/blob/master/Invoke-D365FFOAxDBRestoreFromBACPAC.ps1
#If you are going to download the BACPAC file from the LCS Asset Library, please use this section
$BacpacSasLinkFromLCS = 'https://uswedpl1catalog.blob.core.windows.net%2Fproduct-financeandoperations%2Fd00c14a8-1980-481b-8506-f642cce1fac'
$NewDB = 'Demo20230325' #Database name. No spaces in the name! Do not put here AxDB!
$TempFolder = 'd:\temp\' # 'c:\temp\' #$env:TEMP
#If you are NOT going to download the BACPAC file from the LCS Asset Library, please use this section
#$BacpacSasLinkFromLCS = ''
#$f = Get-ChildItem D:\temp\CFBSSalesbackup.bacpac #Please note that this file should be accessible from the SQL server service account
#$NewDB = $($f.BaseName).Replace(' ','_'); #'AxDB_CTS1005BU2' #Temporary Database name for new AxDB. Use a file name or any meaningful name.
#$NewDB = $($f.BaseName).Replace('-','_'); #'AxDB_CTS1005BU2' #Temporary Database name for new AxDB. Use a file name or any meaningful name.
#############################################
$ErrorActionPreference = "Stop"
#region Installing d365fo.tools and dbatools <--
# This is required by Find-Module, by doing it beforehand, we remove some warning messages
Write-Host "Installing PowerShell modules d365fo.tools and dbatools" -ForegroundColor Yellow
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope AllUsers
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
$modules2Install = @('d365fo.tools','dbatools')
foreach($module in $modules2Install)
{
Write-Host "..working on module" $module -ForegroundColor Yellow
if ($null -eq $(Get-Command -Module $module)) {
Write-Host "....installing module" $module -ForegroundColor Gray
Install-Module -Name $module -SkipPublisherCheck -Scope AllUsers
} else {
Write-Host "....updating module" $module -ForegroundColor Gray
Update-Module -Name $module
}
}
#endregion Installing d365fo.tools and dbatools -->
#region Download bacpac from LCS
if ($BacpacSasLinkFromLCS.StartsWith('http'))
{
Write-Host "Downloading BACPAC from the LCS Asset library" -ForegroundColor Yellow
New-Item -Path $TempFolder -ItemType Directory -Force
$TempFileName = Join-path $TempFolder -ChildPath "$NewDB.bacpac"
Write-Host "..Downloading file" $TempFileName -ForegroundColor Yellow
Invoke-D365InstallAzCopy
Invoke-D365AzCopyTransfer -SourceUri $BacpacSasLinkFromLCS -DestinationUri $TempFileName -ShowOriginalProgress
$f = Get-ChildItem $TempFileName
$NewDB = $($f.BaseName).Replace(' ','_')
}
#endregion Download bacpac from LCS
#region Apply SQL Connection settings <--
Set-DbatoolsConfig -FullName sql.connection.trustcert -Value $true
Set-DbatoolsConfig -FullName sql.connection.encrypt -Value $false
#endregion Apply SQL Connection settings -->
## Stop D365FO instance.
## Optional. You may Import bacpac while D365FO is up and running
## Stopping of D365FO will just improve performance / RAM Memory consumption
Write-Host "Stopping D365FO environment" -ForegroundColor Yellow
Stop-D365Environment
Enable-D365Exception -Verbose
Invoke-D365InstallSqlPackage #Installing modern SqlPackage just in case
## Import bacpac to SQL Database
#Trust SqlServer Certificate
Set-DbatoolsConfig -FullName 'sql.connection.trustcert' -Value $true -Register
If (-not (Test-DbaPath -SqlInstance localhost -Path $($f.FullName)))
{
Write-Warning "Database file $($f.FullName) could not be found by SQL Server. Try to move it to C:\Temp or D:\Temp"
throw "Database file $($f.FullName) could not be found by SQL Server. Try to move it to C:\Temp or D:\Temp"
}
$f | Unblock-File
#region Clean up tables <--
Write-Host "Clean up tables directly from BACPAC file" $($f.FullName) -ForegroundColor Yellow
#Get details about the top 10 tables
#Get-D365BacpacTable -Path $f.FullName -SortSizeDesc -Top 30
#Define all tables that it's safe to remove
[string[]]$Tables2CleanUp = "dbo.DOCUHISTORY","dbo.BATCHJOBHISTORY","dbo.BATCHHISTORY",
"dbo.EVENTCUD","dbo.EVENTINBOX","dbo.EVENTINBOXDATA",
"dbo.WORKFLOWTRACKINGTABLE","dbo.WORKFLOWTRACKINGCOMMENTTABLE","dbo.WORKFLOWTRACKINGARGUMENTTABLE","dbo.WORKFLOWTRACKINGSTATUSTABLE",
"dbo.DMFDEFINITIONGROUPEXECUTION","dbo.DMFSTAGINGEXECUTIONERRORS","dbo.DMFSTAGINGLOG","dbo.DMFSTAGINGLOGDETAILS","dbo.DMFDEFINITIONGROUPEXECUTIONPROGRESS","dbo.DMFSTAGINGVALIDATIONLOG",
"*STAGING*",
"dbo.COSTSHEETCACHE","dbo.INVENTAGINGTMP","dbo.SALESPACKINGSLIPHEADERTMP","dbo.SOURCEDOCUMENTLINESUBLEDGERJOURERRORLOG","dbo.DIMENSIONHASHMESSAGELOG",
"dbo.SYSLASTVALUE","dbo.SYSEMAILHISTORY","dbo.SYSUSERLOG",
"dbo.SYSDATABASELOG",
"dbo.SYSENCRYPTIONLOG","dbo.SYSOUTGOINGEMAILTABLE","dbo.SECURITYOBJECTHISTORY"
#Remove unnecessary tables
$ErrorActionPreference = "SilentlyContinue"
Clear-D365BacpacTableData -Path $f.FullName -ClearFromSource -Table $Tables2CleanUp -Verbose
$ErrorActionPreference = "Stop"
#endregion Clean up tables -->
New-DbaDatabase -SqlInstance localhost -Name $NewDB #-RecoveryModel Simple
#region Fix AutoDrop issue <--
Write-Host "Fix AutoDrop issue in the BACPAC" $($f.FullName) -ForegroundColor Yellow
# Taken from https://gist.github.com/FH-Inway/f485c720b43b72bffaca5fb6c094707e
function Local-FixBacPacModelFile
{
param(
[string]$sourceFile,
[string]$destinationFile,
[int]$flushCnt = 500000
)
if($sourceFile.Equals($destinationFile, [System.StringComparison]::CurrentCultureIgnoreCase))
{
throw "Source and destination files must not be the same."
return;
}
$searchForString = '<Property Name="AutoDrop" Value="True" />';
$replaceWithString = '';
#using performance suggestions from here: https://learn.microsoft.com/en-us/powershell/scripting/dev-cross-plat/performance/script-authoring-considerations
# * use List<String> instead of PS Array @()
# * use StreamReader instead of Get-Content
$buffer = [System.Collections.Generic.List[string]]::new($flushCnt) #much faster than PS array using +=
$buffCnt = 0;
#delete dest file if it already exists.
if(Test-Path -LiteralPath $destinationFile)
{
Remove-Item -LiteralPath $destinationFile -Force;
}
try
{
$stream = [System.IO.StreamReader]::new($sourceFile)
$streamEncoding = $stream.CurrentEncoding;
Write-Verbose "StreamReader.CurrentEncoding: $($streamEncoding.BodyName) $($streamEncoding.CodePage)"
while ($stream.Peek() -ge 0)
{
$line = $stream.ReadLine()
if(-not [string]::IsNullOrEmpty($line))
{
$buffer.Add($line.Replace($searchForString,$replaceWithString));
}
else
{
$buffer.Add($line);
}
$buffCnt++;
if($buffCnt -ge $flushCnt)
{
Write-Verbose "$(Get-Date -Format 'u') Flush buffer"
$buffer | Add-Content -LiteralPath $destinationFile -Encoding UTF8
$buffer = [System.Collections.Generic.List[string]]::new($flushCnt);
$buffCnt = 0;
Write-Verbose "$(Get-Date -Format 'u') Flush complete"
}
}
}
finally
{
$stream.Dispose()
Write-Verbose 'Stream disposed'
}
#flush anything still remaining in the buffer
if($buffCnt -gt 0)
{
$buffer | Add-Content -LiteralPath $destinationFile -Encoding UTF8
$buffer = $null;
$buffCnt = 0;
}
}
$modelFilePath = Join-Path $TempFolder "BacpacModel$($NewDB).xml"
$modelFileUpdatedPath = Join-Path $TempFolder "UpdatedBacpacModel$($NewDB).xml"
Export-D365BacpacModelFile -Path $f.FullName -OutputPath $modelFilePath -Force
Local-FixBacPacModelFile -sourceFile $modelFilePath -destinationFile $modelFileUpdatedPath
Write-Host "Import BACPAC file to the SQL database" $NewDB -ForegroundColor Yellow
Import-D365Bacpac -ImportModeTier1 -BacpacFile $f.FullName -ModelFile $modelFileUpdatedPath -NewDatabaseName $NewDB -Verbose
#endregion Fix AutoDrop issue -->
#Write-Host "Import BACPAC file to the SQL database" $NewDB -ForegroundColor Yellow
#Import-D365Bacpac -ImportModeTier1 -BacpacFile $f.FullName -NewDatabaseName $NewDB -Verbose
## Backup NewDB database (optional)
Write-Host "Backup $NewDB just in case" -ForegroundColor Yellow
Backup-DbaDatabase -SqlInstance localhost -Database $NewDB -Type Full -CompressBackup -BackupFileName "dbname-backuptype-timestamp.bak" -ReplaceInName
## Removing AxDB_orig database and Switching AxDB: NULL <-1- AxDB_original <-2- AxDB <-3- [NewDB]
Write-Host "Stopping D365FO environment and Switching Databases" -ForegroundColor Yellow
Stop-D365Environment
Remove-D365Database -DatabaseName 'AxDB_Original' -Verbose
# Suspend the script for 2.5 seconds
Start-Sleep -Seconds 2.5
Switch-D365ActiveDatabase -NewDatabaseName $NewDB -Verbose
# Suspend the script for 2.5 seconds
Start-Sleep -Seconds 2.5
## Put on hold all Batch Jobs
Write-Host "Disabling all current Batch Jobs" -ForegroundColor Yellow
Invoke-DbaQuery -SqlInstance localhost -Database AxDB -Query "UPDATE BatchJob SET STATUS = 0 WHERE STATUS IN (1,2,5,7) --Set any waiting, executing, ready, or canceling batches to withhold."
## Enable Users except Guest
Write-Host "Enable all users except Guest" -ForegroundColor Yellow
Invoke-DbaQuery -SqlInstance localhost -Database AxDB -Query "Update USERINFO set ENABLE = 1 where ID != 'Guest'"
## Set DB Recovery Model to Simple (Optional)
#Set-DbaDbRecoveryModel -SqlInstance localhost -RecoveryModel Simple -Database AxDB -Confirm:$false
## Enable SQL Change Tracking
Write-Host "Enabling SQL Change Tracking" -ForegroundColor Yellow
Invoke-DbaQuery -SqlInstance localhost -Database AxDB -Query "ALTER DATABASE AxDB SET CHANGE_TRACKING = ON (CHANGE_RETENTION = 6 DAYS, AUTO_CLEANUP = ON)"
## Truncate System tables. Values there will be re-created after AOS start
Write-Host "Truncating System tables. Values there will be re-created after AOS start" -ForegroundColor Yellow
$sqlSysTablesTruncate = @"
TRUNCATE TABLE SYSSERVERCONFIG
TRUNCATE TABLE SYSSERVERSESSIONS
TRUNCATE TABLE SYSCORPNETPRINTERS
TRUNCATE TABLE SYSCLIENTSESSIONS
TRUNCATE TABLE BATCHSERVERCONFIG
TRUNCATE TABLE BATCHSERVERGROUP
"@
Invoke-DbaQuery -SqlInstance localhost -Database AxDB -Query $sqlSysTablesTruncate
#fix retail users
$fixDBusers = @"
use AxDB;
DROP USER IF EXISTS [axdeployextuser];
DROP USER IF EXISTS [axretaildatasyncuser];
DROP USER IF EXISTS [axretailruntimeuser];
DROP USER IF EXISTS [axdbadmin];
GO
CREATE USER [axdeployextuser] FROM LOGIN [axdeployextuser];
CREATE USER [axdbadmin] FROM LOGIN [axdbadmin];
CREATE USER [axretaildatasyncuser] FROM LOGIN [axretaildatasyncuser];
CREATE USER [axretailruntimeuser] FROM LOGIN [axretailruntimeuser];
EXEC sp_addrolemember 'db_owner', 'axdeployextuser';
EXEC sp_addrolemember 'db_owner', 'axdbadmin';
EXEC sp_addrolemember 'db_owner', 'axretaildatasyncuser';
EXEC sp_addrolemember 'db_owner', 'axretailruntimeuser';
GO
"@
Invoke-DbaQuery -SqlInstance localhost -Database AxDB -Query $fixDBusers
## Clean up Power BI settings
Write-Host "Cleaning up Power BI settings" -ForegroundColor Yellow
Invoke-DbaQuery -SqlInstance localhost -Database AxDB -Query "UPDATE PowerBIConfig set CLIENTID = '', APPLICATIONKEY = '', REDIRECTURL = ''"
## Run Database Sync
Write-Host "Executing Database Sync" -ForegroundColor Yellow
Invoke-D365DBSync -ShowOriginalProgress -Verbose
## Backup AxDB database
Write-Host "Backup AxDB" -ForegroundColor Yellow
Backup-DbaDatabase -SqlInstance localhost -Database AxDB -Type Full -CompressBackup -BackupFileName "dbname-$NewDB-backuptype-timestamp.bak" -ReplaceInName
## Promote user as admin and set default tenant (Optional)
#Set-D365Admin -AdminSignInName 'D365Admin@ciellos.com'
## Start D365FO instance
Write-Host "Starting D365FO environment. Then open UI and refresh Data Entities." -ForegroundColor Yellow
Start-D365Environment
## INFO: Get the User email address/tenant
Write-Host "Getting information about users from AxDB" -ForegroundColor Yellow
$sqlGetUsers = @"
select ID, Name, NetworkAlias, NETWORKDOMAIN, Enable from userInfo
where NETWORKALIAS not like '%@contosoax7.onmicrosoft.com'
and NETWORKALIAS not like '%@capintegration01.onmicrosoft.com'
and NETWORKALIAS not like '%@devtesttie.ccsctp.net'
and NETWORKALIAS not like '%@DAXMDSRunner.com'
and NETWORKALIAS not like '%@dynamics.com'
and NETWORKALIAS != ''
"@
Invoke-DbaQuery -SqlInstance localhost -Database AxDB -Query $sqlGetUsers | FT