{"id":77720,"date":"2018-03-22T19:31:29","date_gmt":"2018-03-22T19:31:29","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=77720"},"modified":"2021-08-24T13:39:19","modified_gmt":"2021-08-24T13:39:19","slug":"recover-data-sql-server-aws-rds-help-bacpac","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/database-administration-sql-server\/recover-data-sql-server-aws-rds-help-bacpac\/","title":{"rendered":"Recover Data in SQL Server AWS RDS with the Help of BACPAC"},"content":{"rendered":"<p>Amazon Web Services (AWS) provides highly available, highly managed SQL Server instances through the Relational Database Service (RDS). Using RDS instances greatly reduces much of the administrative need for managing backups, minor upgrades, and monitoring. However, these conveniences come at a cost: limited administrative permissions and functionality. One such limitation that requires a workaround becomes obvious when attempting to <strong>restore a copy of an existing database to the same SQL Server instance<\/strong>.<\/p>\n<p>One probable reason for needing multiple copies of a database on the same server might be to maintain separate database environments for development and testing, especially if using additional servers isn\u2019t an option. Another reason might be data recovery \u2013 if data is deleted or otherwise missing, it may be necessary to temporarily restore a new copy of a database (from either a RDS snapshot or from an existing native backup file), recover the missing data, and then drop the duplicated database.<\/p>\n<p>To demonstrate the RDS limitations and a workaround method, this article will develop a scenario where data has been deleted from a specific table in a RDS database. The objective is to recover the missing data using this method.<\/p>\n<div class=\"note\">\n<p>NOTE: This exercise should not be performed on a production RDS instance until it has been tested on a development instance. See the warning in the \u2018Import the BACPAC\u2019 section.<\/p>\n<\/div>\n<h2>Preparing the Scenario<\/h2>\n<p>For this example, you will create an initial <strong>AdventureWorks2016<\/strong> database from the <a href=\"https:\/\/github.com\/Microsoft\/sql-server-samples\/releases\/tag\/adventureworks\">online sample .bak file<\/a> (our first instance of the database). You\u2019ll then simulate a data loss and attempt to restore a temporary copy of the database (using the same .bak file) from which to recover the missing data.<\/p>\n<p>First, log into your SQL Server RDS instance, using the instance endpoint as the server name:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"505\" height=\"343\" class=\"wp-image-77721\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-94.png\" \/><\/p>\n<p>Next, download the <a href=\"https:\/\/github.com\/Microsoft\/sql-server-samples\/releases\/tag\/adventureworks\">online sample .bak file<\/a> and upload it to your S3 bucket:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1185\" height=\"738\" class=\"wp-image-77722\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-95.png\" \/><\/p>\n<p>Once the file has been uploaded, make a note of the S3 path, in this case backups\/MISC.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1160\" height=\"417\" class=\"wp-image-77723\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-96.png\" \/><\/p>\n<p>You can now initiate a manual database restore using the built-in RDS procedure <strong>rds_restore_database. <\/strong>In Management Studio, run the following statement replacing the path if yours is different:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">--first database restore\r\nEXEC msdb.dbo.rds_restore_database \r\n   @restore_db_name = 'AdventureWorks2016',\r\n   @S3_arn_to_restore_from = 'arn:aws:s3:::backups\/MISC\/AdventureWorks2016.bak';<\/pre>\n<div class=\"note\">\n<p>IMPORTANT: the path value in the <strong>@S3_arn_to_backup_from<\/strong> parameter is case-sensitive. For example, setting the value to &#8216;arn:aws:s3:::backups\/misc\/AdventureWorks2016.bak&#8217; will not work.<\/p>\n<\/div>\n<h2>Adding the SQLSERVER_BACKUP_RESTORE Option<\/h2>\n<p>If Native Backup and Restore Option has not yet been configured for your RDS instance, you will get an error, <em>Database backup\/restore option is not enabled yet or is in the process of being enabled. Please try again later,<\/em> when attempting to use the <strong>rds_restore_database<\/strong> procedure:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"794\" height=\"125\" class=\"wp-image-77724\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-97.png\" \/><\/p>\n<p>Per AWS <a href=\"https:\/\/docs.aws.amazon.com\/AmazonRDS\/latest\/UserGuide\/Appendix.SQLServer.Options.BackupRestore.html\">documentation<\/a>, an option group associated with the RDS instance must have the <em>SQLSERVER_BACKUP_RESTORE<\/em> option added. To do this, you\u2019ll need to create a new option group by navigating to Option groups and clicking <em>Create a group<\/em>. Then enter a name for the option group, a description, the SQL Server engine (Enterprise, Standard, etc.) and version you are using. Then click <em>Create<\/em>:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1103\" height=\"585\" class=\"wp-image-77725\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-98.png\" \/><\/p>\n<p>Once created, select the new option group and click <em>Add option<\/em>:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"494\" height=\"437\" class=\"wp-image-77726\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-99.png\" \/><\/p>\n<p>The only available option is the <em>SQLSERVER_BACKUP_RESTORE<\/em> option, the one that you need. Make sure that <em>Create a new role<\/em> is selected, enter a name for the new <em>IAM role<\/em>, and select an existing or create a new S3 bucket. Select <em>Apply Immediately<\/em> and then click <em>Add Option<\/em>:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"595\" height=\"693\" class=\"wp-image-77727\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-100.png\" \/><\/p>\n<p>Take a look at the new <em>sqlNativeBackup<\/em> policy (<em>IAM\/Policies<\/em>, then select <em>sqlNativeBackup<\/em>) that was automatically created for the new IAM role (<em>rl_sqlserver_backup_restore<\/em>). On the <em>{} JSON<\/em> tab, you can see the <strong>Action<\/strong> allowed on the <strong>Resource<\/strong> (S3 bucket):<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"520\" height=\"334\" class=\"wp-image-77728\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-101.png\" \/><\/p>\n<p>All that is left to do now is to associate the RDS instance with the new option group. Select your instance name from <em>RDS\/Instances<\/em> and choose <em>Modify<\/em> from the <em>Instance actions<\/em> dropdown. Next, scroll down to <em>Database Options\/Option group<\/em> and select the new option group from the dropdown.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"601\" height=\"376\" class=\"wp-image-77729\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-102.png\" \/><\/p>\n<p>Click <em>Continue<\/em>. Select <em>Apply immediately<\/em><strong>,<\/strong> and then <em>Modify DB Instance<\/em>. Even after completing the steps, it may take several minutes to go into effect.<\/p>\n<h2>Checking the Restore Status<\/h2>\n<p>You should now be able to execute the <strong>rds_restore_database <\/strong>procedure successfully. When executed, the <strong>rds_restore_database<\/strong> command returns a summary acknowledgement and status:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1075\" height=\"47\" class=\"wp-image-77730\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-103.png\" \/><\/p>\n<p>At any time during the restore, you can check the status using the <strong>rds_task_status<\/strong> procedure in another query window:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">--check status\r\nEXEC msdb.dbo.rds_task_status @db_name = 'AdventureWorks2016';\r\n<\/pre>\n<p>During the restore, the <em>lifecycle<\/em> column will show a value of <em>IN_PROGRESS<\/em>:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1108\" height=\"41\" class=\"wp-image-77731\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-104.png\" \/><\/p>\n<p>When the restore is complete, the <em>lifecycle<\/em> column will return <em>SUCCESS. <\/em><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1084\" height=\"38\" class=\"wp-image-77732\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-105.png\" \/><\/p>\n<p>The new database will be visible in <em>Object Explorer<\/em>:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"370\" height=\"142\" class=\"wp-image-77733\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-106.png\" \/><\/p>\n<h2>Simulating data loss<\/h2>\n<p>To simulate missing data, delete 500 records from the <strong>DatabaseLog<\/strong> table:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">USE AdventureWorks2016;\r\nDELETE FROM DatabaseLog\r\nWHERE DatabaseLogID &lt;= 500;<\/pre>\n<h2>The Problem<\/h2>\n<p>Now you can demonstrate the problem of trying to create a duplicate database (with a different database name) using the same backup file. Remember, the goal is to recover the missing data that was deleted in the previous step. Attempt to perform the restore again, this time using a different database name:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">--second database restore\r\nEXEC msdb.dbo.rds_restore_database \r\n   @restore_db_name = 'AdventureWorks2016_clone',\r\n   @S3_arn_to_restore_from = 'arn:aws:s3:::backups\/MISC\/AdventureWorks2016.bak';<\/pre>\n<p>The initial status returned looks normal:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1110\" height=\"44\" class=\"wp-image-77734\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-107.png\" \/><\/p>\n<p>\u2026but the restore ends with a lifecycle value of <em>ERROR<\/em> upon checking the <strong>rds_task_status<\/strong> procedure:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">--check status\r\nEXEC msdb.dbo.rds_task_status @db_name = 'AdventureWorks2016_clone';<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1110\" height=\"43\" class=\"wp-image-77735\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-108.png\" \/><\/p>\n<p>The <em>task_info<\/em> field displays the following error message:<em>[2018-01-23 19:24:22.180] Aborted the task because of a task failure or a concurrent RESTORE_DB request.[2018-01-23 19:24:22.210] Task ID 57 (RESTORE_DB) exception: Database AdventureWorks2016_clone cannot be restored because <\/em><strong><em>there is already an existing database with the same file_guids<\/em><\/strong><em> on the instance.<\/em><\/p>\n<h2>File_guids<\/h2>\n<p>You can see that the RDS limitation\u2019s root cause is the inability to allow the same database unique identifier (<em>file_guid<\/em>) more than once on the same instance \u2013 and since the AdventureWorks2016 database\u2019s original <em>file_guids<\/em> are contained in the backup file, any databases restored from it has that same <em>file_guid<\/em>. You would have the same issue even if you attempted a restore from a fresh backup of the database. Database <em>file_guid<\/em>s can be viewed by selecting them from a database\u2019s <strong>sys.database_files<\/strong> table:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">USE AdventureWorks2016;\r\nSELECT name, file_guid FROM sys.database_files; <\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"399\" height=\"57\" class=\"wp-image-77736\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-109.png\" \/><\/p>\n<p>Amazon acknowledges this issue in the <a href=\"https:\/\/docs.aws.amazon.com\/AmazonRDS\/latest\/UserGuide\/SQLServer.Procedural.Importing.html\">Importing and Exporting SQL Server Databases<\/a> online user guide \u2013 which says that it is not possible to create a copy of a database to the same instance. According to the guide:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"659\" height=\"147\" class=\"wp-image-77737\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-110.png\" \/><\/p>\n<p>Restoring a new RDS instance from a snapshot would make missing data available, but might also expose other limitations. For example, using a linked server connection would allow data transfer between two RDS instances, but linked servers appear to be unsupported in RDS at the time of this writing. An attempt to create a linked server generates the error below:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"617\" height=\"196\" class=\"wp-image-77738\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-111.png\" \/><\/p>\n<h2>A Solution<\/h2>\n<p>However, it is possible, using a known workaround, to create and maintain concurrent, identical databases on the same RDS instance. This workaround entails using the <a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/relational-databases\/data-tier-applications\/export-a-data-tier-application\"><strong>Export Data-Tier Application<\/strong><\/a> module in SQL Server Management Studio (SSMS) to create an export of a database to a .<strong>bacpac<\/strong> file. A BACPAC export can contain either an entire database or just specific objects, and can be used for importing data to Azure, AWS, and conventional SQL Server instances \u2013 and also for data archival purposes. A <a href=\"https:\/\/social.technet.microsoft.com\/wiki\/contents\/articles\/2639.how-to-use-data-tier-application-import-and-export-with-a-windows-azure-sql-database.aspx\">TechNet article<\/a> describes a .bacpac file:<\/p>\n<p>\u201c<em>The export file contains all supported database schema objects and table data in a single package. Schema objects include logins, users, schemas, tables, columns, constraints, indexes, views, stored procedures, functions, triggers, and other objects relevant to the definition of a database.\u201d<\/em><\/p>\n<p>When a .bacpac file is generated and imported, the database\u2019s file_guids are reset \u2013 which is exactly what is needed to allow a duplicate database in RDS.<\/p>\n<p>To get a BACPAC export of the <strong>AdventureWorks2016<\/strong> data that is in our original <em>AdventureWorks2016.bak<\/em> backup, you need to restore the backup file to a new instance. You could restore an RDS snapshot (taken before the data was deleted) as a new, temporary RDS instance and then export the .bacpac file from that instance\u2019s copy of <strong>AdventureWorks2016<\/strong>, but for this example, you\u2019ll just restore the original backup file to another existing SQL Server instance. Restore the original <em>AdventureWorks2016.bak<\/em> to a conventional, on-premises SQL Server 2016 instance (SQL2016_DEV) that already exists as a development environment. Depending on your instance, you may need to use <strong>WITH MOVE<\/strong> in the backup statement to change the data and log file locations, if the original <strong>AdventureWorks2016<\/strong> database used a path that doesn\u2019t exist on you SQL2016_DEV server:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">RESTORE DATABASE AdventureWorks2016 FROM DISK = 'D:\\Backup\\AdventureWorks2016.bak'\r\nWITH MOVE 'AdventureWorks2016_Data' TO 'D:\\Data\\AdventureWorks2016_Data.mdf',\r\nMOVE 'AdventureWorks2016_Log' TO 'D:\\Data\\AdventureWorks2016_Log.ldf';<\/pre>\n<p>Once the restore is complete, you can see the new database in Object Explorer:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"230\" height=\"130\" class=\"wp-image-77739\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-112.png\" \/><\/p>\n<p>When you check the <em>file_guids<\/em> of the new database, you\u2019ll see that it has exactly the same <em>file_guids<\/em> as the copy you restored to the RDS instance (the <em>name<\/em> value is the logical name of the file, not the renamed file path name):<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">USE AdventureWorks2016;\r\nSELECT name, file_guid FROM sys.database_files; <\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"396\" height=\"59\" class=\"wp-image-77740\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-113.png\" \/><\/p>\n<h2>Export the BACPAC<\/h2>\n<p>To start the BACPAC export of the newly-restored <strong>AdventureWorks2016<\/strong> database, right-click the <em>AdventureWorks2016<\/em> database, select <em>Tasks<\/em>, then <em>Export Data-tier Application<\/em>:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"720\" height=\"588\" class=\"wp-image-77741\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-114.png\" \/><\/p>\n<div class=\"note\">\n<p>NOTE: SQL Server Management Studio must be a supported version for this step. If not, the BACPAC export will fail. For example, using SQL Server Management Studio 2014 to export a SQL Server 2016 database generates this error:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"621\" height=\"135\" class=\"wp-image-77742\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-115.png\" \/><\/p>\n<\/div>\n<p>On the <em>Introduction<\/em> page, click <em>Next<\/em>. On the <em>Export Settings<\/em> page, click <em>Browse<\/em> to select a filename and location for the BACPAC export. Keep the default location, and name the export <em>AdventureWorks2016.bacpac<\/em>:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"747\" height=\"687\" class=\"wp-image-77743\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-116.png\" \/><\/p>\n<p>Since you only need to recover data from a single object (the <strong>DatabaseLog<\/strong> table), select that object only in the <em>Advanced<\/em> tab. This will minimize the recovery time by reducing the size of the export file.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"745\" height=\"687\" class=\"wp-image-77744\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-117.png\" \/><\/p>\n<p>Click <em>Next<\/em>, and then <em>Finish<\/em>:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"748\" height=\"687\" class=\"wp-image-77745\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-118.png\" \/><\/p>\n<p>Check the summary of the processes on the <em>Results<\/em> page and click <em>Close<\/em>. The BACPAC export is complete.<\/p>\n<p>The next step will be to immediately import the file back to the same instance as a new database in order to reset the <em>file_guid<\/em>.<\/p>\n<h2>Import the BACPAC<\/h2>\n<p><strong>WARNING!<\/strong> You could attempt to import the <em>.bacpac<\/em> file directly back to the RDS instance, but even though RDS supports BACPAC exports (and imports using files generated from the same instance \u2013 see the note at the end of this article), a BACPAC file imported to RDS that was exported from a different instance can fail with the following error (at the time of this writing), <strong>leaving behind an empty database that cannot be easily removed<\/strong>:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"817\" height=\"571\" class=\"wp-image-77746\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-119.png\" \/><\/p>\n<p>Attempting to drop the empty database that is left behind results in the below error:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"784\" height=\"61\" class=\"wp-image-77747\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-120.png\" \/><\/p>\n<p><strong>For this reason, it is highly recommended to <\/strong><a href=\"https:\/\/docs.aws.amazon.com\/AmazonRDS\/latest\/UserGuide\/CHAP_Tutorials.RestoringFromSnapshot.html\"><strong>restore a new RDS instance from a snapshot<\/strong><\/a><strong> to use for practicing the BACPAC import process before performing it on a production system.<\/strong><\/p>\n<p>To avoid the \u2018empty database\u2019 scenario, you will perform the actual BACPAC import to a new database on the on-premises SQL2016_DEV instance, create a native backup from that new database, and then backup and restore it to the RDS instance. That way, you won\u2019t risk a BACPAC import failure.<\/p>\n<p>Right-click on <em>Databases<\/em> in <em>Object Explorer<\/em> and select <em>Import Data-tier Application<\/em>:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"327\" height=\"211\" class=\"wp-image-77748\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-121.png\" \/><\/p>\n<p>On the <em>Introduction<\/em> page, click <em>Next<\/em>. On the <em>Import Settings<\/em> page, browse to the <em>.bacpac<\/em> export file and click <em>Next<\/em>:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"749\" height=\"689\" class=\"wp-image-77749\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-122.png\" \/><\/p>\n<p>On the <em>Database Settings<\/em> page, enter a name for the temporary copy of the database on SQL2016_DEV, click <em>Next<\/em>, and then <em>Finish<\/em>:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"747\" height=\"684\" class=\"wp-image-77750\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-123.png\" \/><\/p>\n<h2>A BACPAC Import Limitation<\/h2>\n<p>You will quickly find out that the BACPAC import process is very sensitive to a few factors &#8211; one of these being full-text indexes. The import process will fail with a \u2018<em>Full-text Search is not installed, or a full-text component cannot be loaded\u2019<\/em> error, if your SQL2016_DEV instance does not have the Full-text search component installed. Why does this generate such an error on tables when no such tables are included in the BACPAC export? This is because, even though the BACPAC export does not export data from excluded objects, the object schema is still part of the export. You can verify this after the unsuccessful BACPAC import.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"834\" height=\"755\" class=\"wp-image-77751\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-124.png\" \/><\/p>\n<p>Since the goal is to recover missing data only and not full-text indexes, you can circumvent this error without installing Full-text Search. Generate a dynamic SQL script to drop all full-text indexes in the <strong>AdventureWorks2016<\/strong> database on the SQL2016_Dev server, execute the script, then export and import the .bacpac file again:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">USE AdventureWorks2016;\r\nSELECT 'DROP FULLTEXT INDEX ON ['+OBJECT_SCHEMA_NAME(object_id)+'].['+OBJECT_NAME(object_id)+'];' as ObjectName \r\nFROM sys.fulltext_indexes;<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"365\" height=\"103\" class=\"wp-image-77752\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-125.png\" \/><\/p>\n<p>Now that you have generated the appropriate DROP statements, copy and execute them:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"416\" height=\"116\" class=\"wp-image-77753\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-126.png\" \/><\/p>\n<p>Export the <em>.bacpac<\/em> file exactly as before, and immediately import it back to SQL2016_DEV to reset the file_guids. The process is successful this time:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"834\" height=\"753\" class=\"wp-image-77754\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-127.png\" \/><\/p>\n<p>Now check the <em>file_guids<\/em> of the new <strong>AdventureWorks2016-clone<\/strong> database:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"426\" height=\"147\" class=\"wp-image-77755\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-128.png\" \/><\/p>\n<p>You will see that the new database has brand new <em>file_guids<\/em> after the BACPAC import. The RDS instance will now accept <strong>AdventureWorks2016-clone<\/strong> as a new database.<\/p>\n<p>Now back up the new database so you can upload it to you S3 bucket:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">BACKUP DATABASE [AdventureWorks2016-clone] TO DISK = 'D:\\BACKUP\\AdventureWorks2016-clone.bak';<\/pre>\n<p>Notice how much smaller the new backup file is now, since you\u2019ve only exported data for one table:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1167\" height=\"729\" class=\"wp-image-77756\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-129.png\" \/><\/p>\n<p>Connect back to the RDS instance and try the database restore in SSMS now that you have reset the <em>file_guids<\/em>:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">--restore bacpac-exported\/imported database\r\nEXEC msdb.dbo.rds_restore_database \r\n   @restore_db_name = 'AdventureWorks2016_clone',\r\n   @S3_arn_to_restore_from = 'arn:aws:s3:::backups\/MISC\/AdventureWorks2016-clone.bak';<\/pre>\n<p>The <strong>rds_task_status<\/strong> procedure returns a lifecycle value of <strong>SUCCESS. <\/strong><\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">--check status\r\nEXEC msdb.dbo.rds_task_status @db_name = 'AdventureWorks2016_clone';<\/pre>\n<p>You can see the new database in the <em>Object Explorer<\/em> pane:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"515\" height=\"38\" class=\"wp-image-77757\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-130.png\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"472\" height=\"112\" class=\"wp-image-77758\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-131.png\" \/><\/p>\n<p>Check object contents to make sure the BACPAC import only pushed data for the required table and not the others. A count check reveals that while the original <strong>AdventureWorks2016<\/strong> database\u2019s <strong>Person.Address<\/strong> table contains many records, the <strong>AdventureWorks2016_clone<\/strong> database\u2019s correlated table contains no records. The <strong>DatabaseLog<\/strong> table\u2019s data is intact, as expected:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">USE AdventureWorks2016;\r\nSELECT COUNT(1) FROM Person.Address;\r\nSELECT COUNT(1) FROM DatabaseLog;<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"159\" height=\"117\" class=\"wp-image-77759\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-132.png\" \/><\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">USE AdventureWorks2016_clone;\r\nSELECT COUNT(1) FROM Person.Address;\r\nSELECT COUNT(1) FROM DatabaseLog;<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"169\" height=\"115\" class=\"wp-image-77760\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-133.png\" \/><\/p>\n<h2>Recover the Missing Data<\/h2>\n<p>You\u2019re now able to recover the missing <strong>DatabaseLog<\/strong> data by joining the original table to the restored one:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">SET IDENTITY_INSERT AdventureWorks2016.dbo.DatabaseLog ON;\r\nINSERT INTO AdventureWorks2016.dbo.DatabaseLog (DatabaseLogID, PostTime, DatabaseUser, [Event], [Schema], [Object], [TSQL], XmlEvent)\r\nSELECT a.DatabaseLogID, a.PostTime, a.DatabaseUser, a.[Event], a.[Schema], a.[Object], a.[TSQL], a.XmlEvent \r\nFROM AdventureWorks2016_clone.dbo.DatabaseLog a\r\nLEFT OUTER JOIN AdventureWorks2016.dbo.DatabaseLog b \r\n ON b.DatabaseLogID = a.DatabaseLogID\r\nWHERE b.DatabaseLogID IS NULL;\r\nSET IDENTITY_INSERT AdventureWorks2016.dbo.DatabaseLog OFF;<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"187\" height=\"56\" class=\"wp-image-77761\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/03\/word-image-134.png\" \/><\/p>\n<h2>Cleanup<\/h2>\n<p>Now that you\u2019ve successfully recovered the missing data, clean up by dropping the duplicated database from the RDS instance. If Multi-AZ (mirroring) is in place for the instance, you\u2019ll have to break the mirror partner before dropping the database:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">USE master;\r\n--break mirroring\r\nALTER DATABASE [AdventureWorks2016_clone] set PARTNER OFF;\r\n--drop database\r\nDROP DATABASE AdventureWorks2016_clone;<\/pre>\n<h2>Summary<\/h2>\n<p>You\u2019ve completed the steps necessary to recover missing data from a database object in a SQL Server RDS instance using the BACPAC export method using these steps. For reference, the instructional steps are condensed here as follows:<\/p>\n<ol>\n<li>Restore a copy of the database (from an existing backup or RDS snapshot) to another SQL Server instance, which can be either a conventional instance or a RDS instance (if it does not already have a copy of the database on it).\n<ul>\n<li><em>NOTE: This instance must be of the same major SQL Server version as the instance that created the database.<\/em><\/li>\n<\/ul>\n<\/li>\n<li>Export that copy of the database as a <em>.bacpac<\/em> file, including only the objects needed, so that the export stays as small as possible.<\/li>\n<li>Import that <em>.bacpac<\/em> file as a new database (can be imported to the same instance it was exported from, as long as it is NOT a RDS instance). This step gives the database a new <em>file_guid<\/em>.<\/li>\n<li>Now that the new database contains a new <em>file_guid<\/em>, back it up to S3.<\/li>\n<li>Restore the backup file in S3 to the original RDS instance as a new, duplicated database (with a new name).<\/li>\n<li>Recover the missing data from the duplicated database.<\/li>\n<\/ol>\n<div class=\"note\">\n<p>NOTE: If you didn\u2019t need to recover data, but simply wanted a copy of an existing RDS database, you could perform a BACPAC export, then import the .bacpac file immediately to the same instance it came from as a database copy with a new name \u2013 the empty database issue does not seem to happen when the BACPAC import is from a file that was exported from the same instance.<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>With Amazon RDS, it\u2019s easy to have a SQL Server running in minutes. There are some limitations, though. Seth Delconte explains a workaround to overcome one of the limitations: the inability to restore a second copy of a database to recover missing data.&hellip;<\/p>\n","protected":false},"author":221920,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[137092,143527,43],"tags":[95506],"coauthors":[48345],"class_list":["post-77720","post","type-post","status-publish","format-standard","hentry","category-aws","category-database-administration-sql-server","category-platform-as-a-service","tag-automate"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/77720","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221920"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=77720"}],"version-history":[{"count":3,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/77720\/revisions"}],"predecessor-version":[{"id":77770,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/77720\/revisions\/77770"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=77720"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=77720"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=77720"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=77720"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}