Skip to content

Commit

Permalink
data_factory_linked_service_sql_server: support key_vault_password (#…
Browse files Browse the repository at this point in the history
…10032)

fixes #6481

Following the same interface introduced at #9928, where we added key_vault_password property on azurerm_data_factory_linked_service_synapse to support passwords stored in Key Vault secret through an ADF linked service, this PR adds the same capability to azurerm_data_factory_linked_service_sql_server.
  • Loading branch information
allantargino committed Jan 13, 2021
1 parent d9b6ffb commit ec4743e
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/datafactory/parse"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)
Expand Down Expand Up @@ -60,6 +61,27 @@ func resourceDataFactoryLinkedServiceSQLServer() *schema.Resource {
ValidateFunc: validation.StringIsNotEmpty,
},

"key_vault_password": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"linked_service_name": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringIsNotEmpty,
},

"secret_name": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringIsNotEmpty,
},
},
},
},

"description": {
Type: schema.TypeString,
Optional: true,
Expand All @@ -76,23 +98,26 @@ func resourceDataFactoryLinkedServiceSQLServer() *schema.Resource {
Type: schema.TypeMap,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
Type: schema.TypeString,
ValidateFunc: validation.StringIsNotEmpty,
},
},

"annotations": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
Type: schema.TypeString,
ValidateFunc: validation.StringIsNotEmpty,
},
},

"additional_properties": {
Type: schema.TypeMap,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
Type: schema.TypeString,
ValidateFunc: validation.StringIsNotEmpty,
},
},
},
Expand Down Expand Up @@ -121,16 +146,15 @@ func resourceDataFactoryLinkedServiceSQLServerCreateUpdate(d *schema.ResourceDat
}
}

sqlServerProperties := &datafactory.SQLServerLinkedServiceTypeProperties{
ConnectionString: d.Get("connection_string").(string),
}

description := d.Get("description").(string)
password := d.Get("key_vault_password").([]interface{})

sqlServerLinkedService := &datafactory.SQLServerLinkedService{
Description: &description,
SQLServerLinkedServiceTypeProperties: sqlServerProperties,
Type: datafactory.TypeSQLServer,
Description: utils.String(d.Get("description").(string)),
SQLServerLinkedServiceTypeProperties: &datafactory.SQLServerLinkedServiceTypeProperties{
ConnectionString: d.Get("connection_string").(string),
Password: expandAzureKeyVaultPassword(password),
},
Type: datafactory.TypeSQLServer,
}

if v, ok := d.GetOk("parameters"); ok {
Expand Down Expand Up @@ -177,31 +201,28 @@ func resourceDataFactoryLinkedServiceSQLServerRead(d *schema.ResourceData, meta
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
defer cancel()

id, err := azure.ParseAzureResourceID(d.Id())
id, err := parse.LinkedServiceID(d.Id())
if err != nil {
return err
}
resourceGroup := id.ResourceGroup
dataFactoryName := id.Path["factories"]
name := id.Path["linkedservices"]

resp, err := client.Get(ctx, resourceGroup, dataFactoryName, name, "")
resp, err := client.Get(ctx, id.ResourceGroup, id.FactoryName, id.Name, "")
if err != nil {
if utils.ResponseWasNotFound(resp.Response) {
d.SetId("")
return nil
}

return fmt.Errorf("Error retrieving Data Factory Linked Service SQL Server %q (Data Factory %q / Resource Group %q): %+v", name, dataFactoryName, resourceGroup, err)
return fmt.Errorf("Error retrieving Data Factory Linked Service SQL Server %q (Data Factory %q / Resource Group %q): %+v", id.Name, id.FactoryName, id.ResourceGroup, err)
}

d.Set("name", resp.Name)
d.Set("resource_group_name", resourceGroup)
d.Set("data_factory_name", dataFactoryName)
d.Set("name", id.Name)
d.Set("resource_group_name", id.ResourceGroup)
d.Set("data_factory_name", id.FactoryName)

sqlServer, ok := resp.Properties.AsSQLServerLinkedService()
if !ok {
return fmt.Errorf("Error classifiying Data Factory Linked Service SQL Server %q (Data Factory %q / Resource Group %q): Expected: %q Received: %q", name, dataFactoryName, resourceGroup, datafactory.TypeSQLServer, *resp.Type)
return fmt.Errorf("Error classifiying Data Factory Linked Service SQL Server %q (Data Factory %q / Resource Group %q): Expected: %q Received: %q", id.Name, id.FactoryName, id.ResourceGroup, datafactory.TypeSQLServer, *resp.Type)
}

d.Set("additional_properties", sqlServer.AdditionalProperties)
Expand All @@ -223,18 +244,24 @@ func resourceDataFactoryLinkedServiceSQLServerRead(d *schema.ResourceData, meta
}
}

connectionString := ""
if properties := sqlServer.SQLServerLinkedServiceTypeProperties; properties != nil {
if properties.ConnectionString != nil {
val, ok := properties.ConnectionString.(string)
if ok {
connectionString = val
if val, ok := properties.ConnectionString.(string); ok {
d.Set("connection_string", val)
} else {
d.Set("connection_string", "")
log.Printf("[DEBUG] Skipping connection string %q since it's not a string", val)
}
}

if password := properties.Password; password != nil {
if keyVaultPassword, ok := password.AsAzureKeyVaultSecretReference(); ok {
if err := d.Set("key_vault_password", flattenAzureKeyVaultPassword(keyVaultPassword)); err != nil {
return fmt.Errorf("setting `key_vault_password`: %+v", err)
}
}
}
}
d.Set("connection_string", connectionString)

return nil
}
Expand All @@ -244,18 +271,15 @@ func resourceDataFactoryLinkedServiceSQLServerDelete(d *schema.ResourceData, met
ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d)
defer cancel()

id, err := azure.ParseAzureResourceID(d.Id())
id, err := parse.LinkedServiceID(d.Id())
if err != nil {
return err
}
resourceGroup := id.ResourceGroup
dataFactoryName := id.Path["factories"]
name := id.Path["linkedservices"]

response, err := client.Delete(ctx, resourceGroup, dataFactoryName, name)
response, err := client.Delete(ctx, id.ResourceGroup, id.FactoryName, id.Name)
if err != nil {
if !utils.ResponseWasNotFound(response) {
return fmt.Errorf("Error deleting Data Factory Linked Service SQL Server %q (Data Factory %q / Resource Group %q): %+v", name, dataFactoryName, resourceGroup, err)
return fmt.Errorf("Error deleting Data Factory Linked Service SQL Server %q (Data Factory %q / Resource Group %q): %+v", id.Name, id.FactoryName, id.ResourceGroup, err)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,24 @@ func TestAccDataFactoryLinkedServiceSQLServer_basic(t *testing.T) {
})
}

func TestAccDataFactoryLinkedServiceSQLServer_KeyVaultReference(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_data_factory_linked_service_sql_server", "test")
r := LinkedServiceSQLServerResource{}

data.ResourceTest(t, r, []resource.TestStep{
{
Config: r.key_vault_reference(data),
Check: resource.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
check.That(data.ResourceName).Key("connection_string").Exists(),
check.That(data.ResourceName).Key("key_vault_password.0.linked_service_name").HasValue("linkkv"),
check.That(data.ResourceName).Key("key_vault_password.0.secret_name").HasValue("secret"),
),
},
data.ImportStep(),
})
}

func (t LinkedServiceSQLServerResource) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) {
id, err := azure.ParseAzureResourceID(state.ID)
if err != nil {
Expand Down Expand Up @@ -141,3 +159,51 @@ resource "azurerm_data_factory_linked_service_sql_server" "test" {
}
`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger)
}

func (LinkedServiceSQLServerResource) key_vault_reference(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
}
data "azurerm_client_config" "current" {}
resource "azurerm_resource_group" "test" {
name = "acctestRG-df-%d"
location = "%s"
}
resource "azurerm_key_vault" "test" {
name = "acctkv%d"
location = azurerm_resource_group.test.location
resource_group_name = azurerm_resource_group.test.name
tenant_id = data.azurerm_client_config.current.tenant_id
sku_name = "standard"
}
resource "azurerm_data_factory" "test" {
name = "acctestdf%d"
location = azurerm_resource_group.test.location
resource_group_name = azurerm_resource_group.test.name
}
resource "azurerm_data_factory_linked_service_key_vault" "test" {
name = "linkkv"
resource_group_name = azurerm_resource_group.test.name
data_factory_name = azurerm_data_factory.test.name
key_vault_id = azurerm_key_vault.test.id
}
resource "azurerm_data_factory_linked_service_sql_server" "test" {
name = "linksqlserver"
resource_group_name = azurerm_resource_group.test.name
data_factory_name = azurerm_data_factory.test.name
connection_string = "Integrated Security=False;Data Source=test;Initial Catalog=test;User ID=test;"
key_vault_password {
linked_service_name = azurerm_data_factory_linked_service_key_vault.test.name
secret_name = "secret"
}
}
`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger)
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,50 @@ resource "azurerm_data_factory_linked_service_sql_server" "example" {
}
```

## Example Usage with Password in Key Vault

```hcl
data "azurerm_client_config" "current" {}
resource "azurerm_resource_group" "example" {
name = "example-resources"
location = "northeurope"
}
resource "azurerm_key_vault" "example" {
name = "example"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
tenant_id = data.azurerm_client_config.current.tenant_id
sku_name = "standard"
}
resource "azurerm_data_factory" "example" {
name = "example"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
}
resource "azurerm_data_factory_linked_service_key_vault" "example" {
name = "kvlink"
resource_group_name = azurerm_resource_group.example.name
data_factory_name = azurerm_data_factory.example.name
key_vault_id = azurerm_key_vault.example.id
}
resource "azurerm_data_factory_linked_service_sql_server" "example" {
name = "example"
resource_group_name = azurerm_resource_group.example.name
data_factory_name = azurerm_data_factory.example.name
connection_string = "Integrated Security=False;Data Source=test;Initial Catalog=test;User ID=test;"
key_vault_password {
linked_service_name = azurerm_data_factory_linked_service_key_vault.example.name
secret_name = "secret"
}
}
```

## Argument Reference

The following arguments are supported:
Expand All @@ -56,6 +100,18 @@ The following arguments are supported:

* `additional_properties` - (Optional) A map of additional properties to associate with the Data Factory Linked Service SQL Server.

* `key_vault_password` - (Optional) A `key_vault_password` block as defined below. Use this argument to store SQL Server password in an existing Key Vault. It needs an existing Key Vault Data Factory Linked Service.

---

A `key_vault_password` block supports the following:

* `linked_service_name` - (Required) Specifies the name of an existing Key Vault Data Factory Linked Service.

* `secret_name` - (Required) Specifies the secret name in Azure Key Vault that stores SQL Server password.

---

## Attributes Reference

The following attributes are exported:
Expand Down

0 comments on commit ec4743e

Please sign in to comment.