diff --git a/azurerm/internal/services/datafactory/data_factory.go b/azurerm/internal/services/datafactory/data_factory.go index 3a4acd2905ec..2d8bf5604af0 100644 --- a/azurerm/internal/services/datafactory/data_factory.go +++ b/azurerm/internal/services/datafactory/data_factory.go @@ -218,7 +218,7 @@ func suppressJsonOrderingDifference(_, old, new string, _ *pluginsdk.ResourceDat return utils.NormalizeJson(old) == utils.NormalizeJson(new) } -func expandAzureKeyVaultPassword(input []interface{}) *datafactory.AzureKeyVaultSecretReference { +func expandAzureKeyVaultSecretReference(input []interface{}) *datafactory.AzureKeyVaultSecretReference { if len(input) == 0 || input[0] == nil { return nil } @@ -234,7 +234,25 @@ func expandAzureKeyVaultPassword(input []interface{}) *datafactory.AzureKeyVault } } -func flattenAzureKeyVaultPassword(secretReference *datafactory.AzureKeyVaultSecretReference) []interface{} { +func flattenAzureKeyVaultConnectionString(input map[string]interface{}) []interface{} { + if input == nil { + return nil + } + + parameters := make(map[string]interface{}) + + if v, ok := input["store"].(map[string]interface{}); ok { + if v != nil { + parameters["linked_service_name"] = v["referenceName"].(string) + } + } + + parameters["secret_name"] = input["secretName"] + + return []interface{}{parameters} +} + +func flattenAzureKeyVaultSecretReference(secretReference *datafactory.AzureKeyVaultSecretReference) []interface{} { if secretReference == nil { return nil } diff --git a/azurerm/internal/services/datafactory/data_factory_linked_service_azure_databricks_resource.go b/azurerm/internal/services/datafactory/data_factory_linked_service_azure_databricks_resource.go index 047ec35f60f1..26bef3dd98b8 100644 --- a/azurerm/internal/services/datafactory/data_factory_linked_service_azure_databricks_resource.go +++ b/azurerm/internal/services/datafactory/data_factory_linked_service_azure_databricks_resource.go @@ -300,7 +300,7 @@ func resourceDataFactoryLinkedServiceDatabricksCreateUpdate(d *pluginsdk.Resourc if len(accessTokenKeyVaultAuth) > 0 && accessTokenKeyVaultAuth[0] != nil { databricksProperties = &datafactory.AzureDatabricksLinkedServiceTypeProperties{ - AccessToken: expandAzureKeyVaultPassword(accessTokenKeyVaultAuth), + AccessToken: expandAzureKeyVaultSecretReference(accessTokenKeyVaultAuth), } } @@ -466,7 +466,7 @@ func resourceDataFactoryLinkedServiceDatabricksRead(d *pluginsdk.ResourceData, m // We only process AzureKeyVaultSecreReference because a string based access token is masked with asterisks in the GET response // so we can't set it if keyVaultPassword, ok := accessToken.AsAzureKeyVaultSecretReference(); ok { - if err := d.Set("key_vault_password", flattenAzureKeyVaultPassword(keyVaultPassword)); err != nil { + if err := d.Set("key_vault_password", flattenAzureKeyVaultSecretReference(keyVaultPassword)); err != nil { return fmt.Errorf("setting `key_vault_password`: %+v", err) } } diff --git a/azurerm/internal/services/datafactory/data_factory_linked_service_azure_file_storage_resource.go b/azurerm/internal/services/datafactory/data_factory_linked_service_azure_file_storage_resource.go index f3e7d797b9ee..122c48808fc9 100644 --- a/azurerm/internal/services/datafactory/data_factory_linked_service_azure_file_storage_resource.go +++ b/azurerm/internal/services/datafactory/data_factory_linked_service_azure_file_storage_resource.go @@ -200,7 +200,7 @@ func resourceDataFactoryLinkedServiceAzureFileStorageCreateUpdate(d *pluginsdk.R if v, ok := d.GetOk("key_vault_password"); ok { password := v.([]interface{}) - fileStorageProperties.Password = expandAzureKeyVaultPassword(password) + fileStorageProperties.Password = expandAzureKeyVaultSecretReference(password) } if v, ok := d.GetOk("additional_properties"); ok { @@ -268,7 +268,7 @@ func resourceDataFactoryLinkedServiceAzureFileStorageRead(d *pluginsdk.ResourceD if password := fileStorage.Password; password != nil { if keyVaultPassword, ok := password.AsAzureKeyVaultSecretReference(); ok { - if err := d.Set("key_vault_password", flattenAzureKeyVaultPassword(keyVaultPassword)); err != nil { + if err := d.Set("key_vault_password", flattenAzureKeyVaultSecretReference(keyVaultPassword)); err != nil { return fmt.Errorf("setting `key_vault_password`: %+v", err) } } diff --git a/azurerm/internal/services/datafactory/data_factory_linked_service_azure_sql_database_resource.go b/azurerm/internal/services/datafactory/data_factory_linked_service_azure_sql_database_resource.go index 0e1908601bfb..2625afb6824a 100644 --- a/azurerm/internal/services/datafactory/data_factory_linked_service_azure_sql_database_resource.go +++ b/azurerm/internal/services/datafactory/data_factory_linked_service_azure_sql_database_resource.go @@ -197,7 +197,7 @@ func resourceDataFactoryLinkedServiceAzureSQLDatabaseCreateUpdate(d *pluginsdk.R if v, ok := d.GetOk("key_vault_password"); ok { password := v.([]interface{}) - sqlDatabaseProperties.Password = expandAzureKeyVaultPassword(password) + sqlDatabaseProperties.Password = expandAzureKeyVaultSecretReference(password) } azureSQLDatabaseLinkedService := &datafactory.AzureSQLDatabaseLinkedService{ @@ -292,7 +292,7 @@ func resourceDataFactoryLinkedServiceAzureSQLDatabaseRead(d *pluginsdk.ResourceD if password := sql.Password; password != nil { if keyVaultPassword, ok := password.AsAzureKeyVaultSecretReference(); ok { - if err := d.Set("key_vault_password", flattenAzureKeyVaultPassword(keyVaultPassword)); err != nil { + if err := d.Set("key_vault_password", flattenAzureKeyVaultSecretReference(keyVaultPassword)); err != nil { return fmt.Errorf("setting `key_vault_password`: %+v", err) } } diff --git a/azurerm/internal/services/datafactory/data_factory_linked_service_snowflake_resource.go b/azurerm/internal/services/datafactory/data_factory_linked_service_snowflake_resource.go index 050e05c41801..dd28dee8cbe2 100644 --- a/azurerm/internal/services/datafactory/data_factory_linked_service_snowflake_resource.go +++ b/azurerm/internal/services/datafactory/data_factory_linked_service_snowflake_resource.go @@ -151,7 +151,7 @@ func resourceDataFactoryLinkedServiceSnowflakeCreateUpdate(d *pluginsdk.Resource Description: utils.String(d.Get("description").(string)), SnowflakeLinkedServiceTypeProperties: &datafactory.SnowflakeLinkedServiceTypeProperties{ ConnectionString: d.Get("connection_string").(string), - Password: expandAzureKeyVaultPassword(password), + Password: expandAzureKeyVaultSecretReference(password), }, Type: datafactory.TypeBasicLinkedServiceTypeSnowflake, } @@ -255,7 +255,7 @@ func resourceDataFactoryLinkedServiceSnowflakeRead(d *pluginsdk.ResourceData, me if password := properties.Password; password != nil { if keyVaultPassword, ok := password.AsAzureKeyVaultSecretReference(); ok { - if err := d.Set("key_vault_password", flattenAzureKeyVaultPassword(keyVaultPassword)); err != nil { + if err := d.Set("key_vault_password", flattenAzureKeyVaultSecretReference(keyVaultPassword)); err != nil { return fmt.Errorf("setting `key_vault_password`: %+v", err) } } diff --git a/azurerm/internal/services/datafactory/data_factory_linked_service_sql_server_resource.go b/azurerm/internal/services/datafactory/data_factory_linked_service_sql_server_resource.go index cb8c67ee919d..da3a172701eb 100644 --- a/azurerm/internal/services/datafactory/data_factory_linked_service_sql_server_resource.go +++ b/azurerm/internal/services/datafactory/data_factory_linked_service_sql_server_resource.go @@ -2,7 +2,6 @@ package datafactory import ( "fmt" - "log" "time" "github.com/Azure/azure-sdk-for-go/services/datafactory/mgmt/2018-06-01/datafactory" @@ -55,11 +54,34 @@ func resourceDataFactoryLinkedServiceSQLServer() *pluginsdk.Resource { "connection_string": { Type: pluginsdk.TypeString, - Required: true, + Optional: true, + ExactlyOneOf: []string{"connection_string", "key_vault_connection_string"}, DiffSuppressFunc: azureRmDataFactoryLinkedServiceConnectionStringDiff, ValidateFunc: validation.StringIsNotEmpty, }, + "key_vault_connection_string": { + Type: pluginsdk.TypeList, + Optional: true, + ExactlyOneOf: []string{"connection_string", "key_vault_connection_string"}, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "linked_service_name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "secret_name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + "key_vault_password": { Type: pluginsdk.TypeList, Optional: true, @@ -150,12 +172,19 @@ func resourceDataFactoryLinkedServiceSQLServerCreateUpdate(d *pluginsdk.Resource sqlServerLinkedService := &datafactory.SQLServerLinkedService{ Description: utils.String(d.Get("description").(string)), SQLServerLinkedServiceTypeProperties: &datafactory.SQLServerLinkedServiceTypeProperties{ - ConnectionString: d.Get("connection_string").(string), - Password: expandAzureKeyVaultPassword(password), + Password: expandAzureKeyVaultSecretReference(password), }, Type: datafactory.TypeBasicLinkedServiceTypeSQLServer, } + if v, ok := d.GetOk("connection_string"); ok { + sqlServerLinkedService.SQLServerLinkedServiceTypeProperties.ConnectionString = v.(string) + } + + if v, ok := d.GetOk("key_vault_connection_string"); ok { + sqlServerLinkedService.SQLServerLinkedServiceTypeProperties.ConnectionString = expandAzureKeyVaultSecretReference(v.([]interface{})) + } + if v, ok := d.GetOk("parameters"); ok { sqlServerLinkedService.Parameters = expandDataFactoryParameters(v.(map[string]interface{})) } @@ -245,17 +274,20 @@ func resourceDataFactoryLinkedServiceSQLServerRead(d *pluginsdk.ResourceData, me if properties := sqlServer.SQLServerLinkedServiceTypeProperties; properties != nil { if properties.ConnectionString != nil { - if val, ok := properties.ConnectionString.(string); ok { + if val, ok := properties.ConnectionString.(map[string]interface{}); ok { + if err := d.Set("key_vault_connection_string", flattenAzureKeyVaultConnectionString(val)); err != nil { + return fmt.Errorf("setting `key_vault_connection_string`: %+v", err) + } + } else 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) + return fmt.Errorf("setting `connection_string`: %+v", err) } } if password := properties.Password; password != nil { if keyVaultPassword, ok := password.AsAzureKeyVaultSecretReference(); ok { - if err := d.Set("key_vault_password", flattenAzureKeyVaultPassword(keyVaultPassword)); err != nil { + if err := d.Set("key_vault_password", flattenAzureKeyVaultSecretReference(keyVaultPassword)); err != nil { return fmt.Errorf("setting `key_vault_password`: %+v", err) } } diff --git a/azurerm/internal/services/datafactory/data_factory_linked_service_sql_server_resource_test.go b/azurerm/internal/services/datafactory/data_factory_linked_service_sql_server_resource_test.go index e6e1438fc812..e240a961d4a4 100644 --- a/azurerm/internal/services/datafactory/data_factory_linked_service_sql_server_resource_test.go +++ b/azurerm/internal/services/datafactory/data_factory_linked_service_sql_server_resource_test.go @@ -47,7 +47,7 @@ func TestAccDataFactoryLinkedServiceSQLServer_basic(t *testing.T) { }) } -func TestAccDataFactoryLinkedServiceSQLServer_KeyVaultReference(t *testing.T) { +func TestAccDataFactoryLinkedServiceSQLServer_PasswordKeyVaultReference(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_data_factory_linked_service_sql_server", "test") r := LinkedServiceSQLServerResource{} @@ -65,6 +65,25 @@ func TestAccDataFactoryLinkedServiceSQLServer_KeyVaultReference(t *testing.T) { }) } +func TestAccDataFactoryLinkedServiceSQLServer_ConnectionStringKeyVaultReference(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_data_factory_linked_service_sql_server", "test") + r := LinkedServiceSQLServerResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.connection_string_key_vault_reference(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("key_vault_connection_string.0.linked_service_name").HasValue("linkkv"), + check.That(data.ResourceName).Key("key_vault_connection_string.0.secret_name").HasValue("connection_string"), + 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("password"), + ), + }, + data.ImportStep(), + }) +} + func (t LinkedServiceSQLServerResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { id, err := azure.ParseAzureResourceID(state.ID) if err != nil { @@ -205,3 +224,55 @@ resource "azurerm_data_factory_linked_service_sql_server" "test" { } `, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) } + +func (LinkedServiceSQLServerResource) connection_string_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 + + key_vault_connection_string { + linked_service_name = azurerm_data_factory_linked_service_key_vault.test.name + secret_name = "connection_string" + } + + key_vault_password { + linked_service_name = azurerm_data_factory_linked_service_key_vault.test.name + secret_name = "password" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) +} diff --git a/azurerm/internal/services/datafactory/data_factory_linked_service_synapse_resource.go b/azurerm/internal/services/datafactory/data_factory_linked_service_synapse_resource.go index b85e555ad54b..4047622a8828 100644 --- a/azurerm/internal/services/datafactory/data_factory_linked_service_synapse_resource.go +++ b/azurerm/internal/services/datafactory/data_factory_linked_service_synapse_resource.go @@ -151,7 +151,7 @@ func resourceDataFactoryLinkedServiceSynapseCreateUpdate(d *pluginsdk.ResourceDa Description: utils.String(d.Get("description").(string)), AzureSQLDWLinkedServiceTypeProperties: &datafactory.AzureSQLDWLinkedServiceTypeProperties{ ConnectionString: d.Get("connection_string").(string), - Password: expandAzureKeyVaultPassword(password), + Password: expandAzureKeyVaultSecretReference(password), }, Type: datafactory.TypeBasicLinkedServiceTypeAzureSQLDW, } @@ -253,7 +253,7 @@ func resourceDataFactoryLinkedServiceSynapseRead(d *pluginsdk.ResourceData, meta } } - if err := d.Set("key_vault_password", flattenAzureKeyVaultPassword(properties.Password)); err != nil { + if err := d.Set("key_vault_password", flattenAzureKeyVaultSecretReference(properties.Password)); err != nil { return fmt.Errorf("setting `key_vault_password`: %+v", err) } } diff --git a/website/docs/r/data_factory_linked_service_sql_server.html.markdown b/website/docs/r/data_factory_linked_service_sql_server.html.markdown index f953708e36da..14b86be2d105 100644 --- a/website/docs/r/data_factory_linked_service_sql_server.html.markdown +++ b/website/docs/r/data_factory_linked_service_sql_server.html.markdown @@ -89,7 +89,7 @@ The following arguments are supported: * `data_factory_name` - (Required) The Data Factory name in which to associate the Linked Service with. Changing this forces a new resource. -* `connection_string` - (Required) The connection string in which to authenticate with the SQL Server. +* `connection_string` - (Optional) The connection string in which to authenticate with the SQL Server. Exactly one of either `connection_string` or `key_vault_connection_string` is required. * `description` - (Optional) The description for the Data Factory Linked Service SQL Server. @@ -101,10 +101,20 @@ 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_connection_string` - (Optional) A `key_vault_connection_string` block as defined below. Use this argument to store SQL Server connection string in an existing Key Vault. It needs an existing Key Vault Data Factory Linked Service. Exactly one of either `connection_string` or `key_vault_connection_string` is required. + * `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_connection_string` 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 connection string. + +--- + A `key_vault_password` block supports the following: * `linked_service_name` - (Required) Specifies the name of an existing Key Vault Data Factory Linked Service.