Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

azurerm_sql_database - support for importing from bacpac #972

Merged
merged 10 commits into from
Jun 19, 2018
90 changes: 90 additions & 0 deletions azurerm/resource_arm_sql_database.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package azurerm
import (
"fmt"
"log"
"strings"
"time"

"github.com/Azure/azure-sdk-for-go/services/sql/mgmt/2015-05-01-preview/sql"
Expand Down Expand Up @@ -57,6 +58,56 @@ func resourceArmSqlDatabase() *schema.Resource {
DiffSuppressFunc: ignoreCaseDiffSuppressFunc,
},

"import": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"storage_uri": {
Type: schema.TypeString,
Required: true,
},
"storage_key": {
Type: schema.TypeString,
Required: true,
Sensitive: true,
},
"storage_key_type": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{
"StorageAccessKey", "SharedAccessKey",
}, true),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this true represents the strings being case-insensitive; as such can we also add DiffSuppressFunc: ignoreCaseDiffSuppressFunc here to handle the casing being different?

},
"administrator_login": {
Type: schema.TypeString,
Required: true,
},
"administrator_login_password": {
Type: schema.TypeString,
Required: true,
Sensitive: true,
},
"authentication_type": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{
"SQL", "ADPassword",
}, true),
},
"operation_mode": {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hbuckle If there is only one value, does this need to be required?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 - we should be able to make this Optional with a Default value instead :)

Type: schema.TypeString,
Optional: true,
Default: "Import",
ValidateFunc: validation.StringInSlice([]string{
"Import",
}, true),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(same here)

},
},
},
},

"source_database_id": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -238,6 +289,27 @@ func resourceArmSqlDatabaseCreateUpdate(d *schema.ResourceData, meta interface{}
return err
}

if _, ok := d.GetOk("import"); ok {
if !strings.EqualFold(createMode, "default") {
return fmt.Errorf("import can only be used when create_mode is Default")
}
importParameters := expandAzureRmSqlDatabaseImport(d)
importFuture, err := client.CreateImportOperation(ctx, resourceGroup, serverName, name, importParameters)
if err != nil {
return err
}

// this is set in config.go, but something sets
// it back to 15 minutes, which isn't long enough
// for most imports
client.Client.PollingDuration = 60 * time.Minute
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from what I recall the 15 minute reset is pulled from the API's retry interval recommendation * the number of retries allowed - I've been meaning to investigate this further


err = importFuture.WaitForCompletion(ctx, client.Client)
if err != nil {
return err
}
}

resp, err := client.Get(ctx, resourceGroup, serverName, name, "")
if err != nil {
return err
Expand Down Expand Up @@ -356,3 +428,21 @@ func flattenEncryptionStatus(encryption *[]sql.TransparentDataEncryption) string

return ""
}

func expandAzureRmSqlDatabaseImport(d *schema.ResourceData) sql.ImportExtensionRequest {
v := d.Get("import")
dbimportRefs := v.([]interface{})
dbimportRef := dbimportRefs[0].(map[string]interface{})
return sql.ImportExtensionRequest{
Name: utils.String("terraform"),
ImportExtensionProperties: &sql.ImportExtensionProperties{
StorageKeyType: sql.StorageKeyType(dbimportRef["storage_key_type"].(string)),
StorageKey: utils.String(dbimportRef["storage_key"].(string)),
StorageURI: utils.String(dbimportRef["storage_uri"].(string)),
AdministratorLogin: utils.String(dbimportRef["administrator_login"].(string)),
AdministratorLoginPassword: utils.String(dbimportRef["administrator_login_password"].(string)),
AuthenticationType: sql.AuthenticationType(dbimportRef["authentication_type"].(string)),
OperationMode: utils.String(dbimportRef["operation_mode"].(string)),
},
}
}
89 changes: 89 additions & 0 deletions azurerm/resource_arm_sql_database_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,25 @@ func testCheckAzureRMSqlDatabaseDisappears(name string) resource.TestCheckFunc {
}
}

func TestAccAzureRMSqlDatabase_bacpac(t *testing.T) {
ri := acctest.RandInt()
config := testAccAzureRMSqlDatabase_bacpac(ri, testLocation())

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMSqlDatabaseDestroy,
Steps: []resource.TestStep{
{
Config: config,
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMSqlDatabaseExists("azurerm_sql_database.test"),
),
},
},
})
}

func testAccAzureRMSqlDatabase_basic(rInt int, location string) string {
return fmt.Sprintf(`
resource "azurerm_resource_group" "test" {
Expand Down Expand Up @@ -496,3 +515,73 @@ resource "azurerm_sql_database" "test" {
}
`, rInt, location, rInt, rInt)
}

func testAccAzureRMSqlDatabase_bacpac(rInt int, location string) string {
return fmt.Sprintf(`
resource "azurerm_resource_group" "test" {
name = "acctestRG_%d"
location = "%s"
}

resource "azurerm_storage_account" "test" {
name = "accsa%d"
resource_group_name = "${azurerm_resource_group.test.name}"
location = "${azurerm_resource_group.test.location}"
account_tier = "Standard"
account_replication_type = "LRS"
}

resource "azurerm_storage_container" "test" {
name = "bacpac"
resource_group_name = "${azurerm_resource_group.test.name}"
storage_account_name = "${azurerm_storage_account.test.name}"
container_access_type = "private"
}

resource "azurerm_storage_blob" "test" {
name = "test.bacpac"
resource_group_name = "${azurerm_resource_group.test.name}"
storage_account_name = "${azurerm_storage_account.test.name}"
storage_container_name = "${azurerm_storage_container.test.name}"
type = "block"
source = "testdata/sql_import.bacpac"
}

resource "azurerm_sql_server" "test" {
name = "acctestsqlserver%d"
resource_group_name = "${azurerm_resource_group.test.name}"
location = "${azurerm_resource_group.test.location}"
version = "12.0"
administrator_login = "mradministrator"
administrator_login_password = "thisIsDog11"
}

resource "azurerm_sql_firewall_rule" "test" {
name = "allowazure"
resource_group_name = "${azurerm_resource_group.test.name}"
server_name = "${azurerm_sql_server.test.name}"
start_ip_address = "0.0.0.0"
end_ip_address = "0.0.0.0"
}

resource "azurerm_sql_database" "test" {
name = "acctestdb%d"
resource_group_name = "${azurerm_resource_group.test.name}"
server_name = "${azurerm_sql_server.test.name}"
location = "${azurerm_resource_group.test.location}"
edition = "Standard"
collation = "SQL_Latin1_General_CP1_CI_AS"
max_size_bytes = "1073741824"
requested_service_objective_name = "S0"

import {
storage_uri = "${azurerm_storage_blob.test.url}"
storage_key = "${azurerm_storage_account.test.primary_access_key}"
storage_key_type = "StorageAccessKey"
administrator_login = "${azurerm_sql_server.test.administrator_login}"
administrator_login_password = "${azurerm_sql_server.test.administrator_login_password}"
authentication_type = "SQL"
}
}
`, rInt, location, rInt, rInt, rInt)
}
Binary file added azurerm/testdata/sql_import.bacpac
Binary file not shown.
12 changes: 12 additions & 0 deletions website/docs/r/sql_database.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ The following arguments are supported:

* `create_mode` - (Optional) Specifies the type of database to create. Defaults to `Default`. See below for the accepted values/

* `import` - (Optional) A Database Import block as documented below. `create_mode` must be set to `Default`.

* `source_database_id` - (Optional) The URI of the source database if `create_mode` value is not `Default`.

* `restore_point_in_time` - (Optional) The point in time for the restore. Only applies if `create_mode` is `PointInTimeRestore` e.g. 2013-11-08T22:00:40Z
Expand All @@ -73,6 +75,16 @@ The following arguments are supported:

* `tags` - (Optional) A mapping of tags to assign to the resource.

`import` supports the following:

* `storage_uri` - (Required) Specifies the blob URI of the .bacpac file.
* `storage_key` - (Required) Specifies the access key for the storage account.
* `storage_key_type` - (Required) Specifies the type of access key for the storage account. Valid values are `StorageAccessKey` or `SharedAccessKey`.
* `administrator_login` - (Required) Specifies the name of the SQL administrator.
* `administrator_login_password` - (Required) Specifies the password of the SQL administrator.
* `authentication_type` - (Required) Specifies the type of authentication used to access the server. Valid values are `SQL` or `ADPassword`.
* `operation_mode` - (Optional) Specifies the type of import operation being performed. The only allowable value is `Import`.

## Attributes Reference

The following attributes are exported:
Expand Down