From c4dec2e617f7a385b0352d78cefec983db96e97d Mon Sep 17 00:00:00 2001 From: Hong Viet Le Date: Sun, 2 May 2021 19:53:48 +0200 Subject: [PATCH] New Resource: azurerm_data_factory_linked_service_odata --- ...a_factory_linked_service_odata_resource.go | 269 ++++++++++++++++++ ...tory_linked_service_odata_resource_test.go | 237 +++++++++++++++ .../services/datafactory/registration.go | 1 + ...factory_linked_service_odata.html.markdown | 102 +++++++ 4 files changed, 609 insertions(+) create mode 100644 azurerm/internal/services/datafactory/data_factory_linked_service_odata_resource.go create mode 100644 azurerm/internal/services/datafactory/data_factory_linked_service_odata_resource_test.go create mode 100644 website/docs/r/data_factory_linked_service_odata.html.markdown diff --git a/azurerm/internal/services/datafactory/data_factory_linked_service_odata_resource.go b/azurerm/internal/services/datafactory/data_factory_linked_service_odata_resource.go new file mode 100644 index 0000000000000..d43f1a52e010b --- /dev/null +++ b/azurerm/internal/services/datafactory/data_factory_linked_service_odata_resource.go @@ -0,0 +1,269 @@ +package datafactory + +import ( + "fmt" + "time" + + "github.com/Azure/azure-sdk-for-go/services/datafactory/mgmt/2018-06-01/datafactory" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/datafactory/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmDataFactoryLinkedServiceOData() *schema.Resource { + return &schema.Resource{ + Create: resourceArmDataFactoryLinkedServiceODataCreateUpdate, + Read: resourceArmDataFactoryLinkedServiceODataRead, + Update: resourceArmDataFactoryLinkedServiceODataCreateUpdate, + Delete: resourceArmDataFactoryLinkedServiceODataDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Read: schema.DefaultTimeout(5 * time.Minute), + Update: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(30 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.LinkedServiceDatasetName, + }, + + "data_factory_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.DataFactoryName(), + }, + + // There's a bug in the Azure API where this is returned in lower-case + // BUG: https://github.com/Azure/azure-rest-api-specs/issues/5788 + "resource_group_name": azure.SchemaResourceGroupNameDiffSuppress(), + + "url": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "basic_authentication": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "username": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "password": { + Type: schema.TypeString, + Required: true, + Sensitive: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + + "description": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "integration_runtime_name": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "annotations": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + + "parameters": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + + "additional_properties": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + } +} + +func resourceArmDataFactoryLinkedServiceODataCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).DataFactory.LinkedServiceClient + ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) + defer cancel() + + name := d.Get("name").(string) + dataFactoryName := d.Get("data_factory_name").(string) + resourceGroup := d.Get("resource_group_name").(string) + + if d.IsNewResource() { + existing, err := client.Get(ctx, resourceGroup, dataFactoryName, name, "") + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of existing Data Factory Linked Service OData Anonymous %q (Data Factory %q / Resource Group %q): %+v", name, dataFactoryName, resourceGroup, err) + } + } + + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_data_factory_linked_service_odata", *existing.ID) + } + } + + description := d.Get("description").(string) + + odataLinkedService := &datafactory.ODataLinkedService{ + Description: &description, + Type: datafactory.TypeOData, + } + + odataLinkedService.ODataLinkedServiceTypeProperties = authenticationProperties(d) + + return resourceArmDataFactoryLinkedServiceODataRead(d, meta) +} + +func resourceArmDataFactoryLinkedServiceODataRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).DataFactory.LinkedServiceClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := azure.ParseAzureResourceID(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, "") + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + + return fmt.Errorf("Error retrieving Data Factory Linked Service OData %q (Data Factory %q / Resource Group %q): %+v", name, dataFactoryName, resourceGroup, err) + } + + d.Set("name", resp.Name) + d.Set("resource_group_name", resourceGroup) + d.Set("data_factory_name", dataFactoryName) + + odata, ok := resp.Properties.AsODataLinkedService() + if !ok { + return fmt.Errorf("Error classifiying Data Factory Linked Service OData %q (Data Factory %q / Resource Group %q): Expected: %q Received: %q", name, dataFactoryName, resourceGroup, datafactory.TypeWeb, *resp.Type) + } + + props := odata.ODataLinkedServiceTypeProperties + d.Set("url", props.URL) + if props.AuthenticationType == datafactory.ODataAuthenticationTypeBasic { + d.Set("basic_authentication", + map[string]interface{}{ + "username": props.UserName, + "password": props.Password, + }) + } + + d.Set("additional_properties", odata.AdditionalProperties) + d.Set("description", odata.Description) + + annotations := flattenDataFactoryAnnotations(odata.Annotations) + if err := d.Set("annotations", annotations); err != nil { + return fmt.Errorf("Error setting `annotations`: %+v", err) + } + + parameters := flattenDataFactoryParameters(odata.Parameters) + if err := d.Set("parameters", parameters); err != nil { + return fmt.Errorf("Error setting `parameters`: %+v", err) + } + + if connectVia := odata.ConnectVia; connectVia != nil { + if connectVia.ReferenceName != nil { + d.Set("integration_runtime_name", connectVia.ReferenceName) + } + } + + return nil +} + +func resourceArmDataFactoryLinkedServiceODataDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).DataFactory.LinkedServiceClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := azure.ParseAzureResourceID(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) + if err != nil { + if !utils.ResponseWasNotFound(response) { + return fmt.Errorf("Error deleting Data Factory Linked Service OData %q (Data Factory %q / Resource Group %q): %+v", name, dataFactoryName, resourceGroup, err) + } + } + + return nil +} + +func authenticationProperties(d *schema.ResourceData) *datafactory.ODataLinkedServiceTypeProperties { + + url := d.Get("url").(string) + + basic_authentication := d.Get("basic_authentication").([]interface{}) + if basic_authentication != nil { + raw := basic_authentication[0].(map[string]interface{}) + username := raw["username"].(string) + password := raw["password"].(string) + passwordSecureString := datafactory.SecureString{ + Value: &password, + Type: datafactory.TypeSecureString, + } + return &datafactory.ODataLinkedServiceTypeProperties{ + AuthenticationType: datafactory.ODataAuthenticationType(datafactory.ODataAuthenticationTypeBasic), + URL: utils.String(url), + UserName: username, + Password: &passwordSecureString, + } + } + + // default: anonymous authentication + return &datafactory.ODataLinkedServiceTypeProperties{ + AuthenticationType: datafactory.ODataAuthenticationType(datafactory.ODataAuthenticationTypeAnonymous), + URL: utils.String(url), + } +} diff --git a/azurerm/internal/services/datafactory/data_factory_linked_service_odata_resource_test.go b/azurerm/internal/services/datafactory/data_factory_linked_service_odata_resource_test.go new file mode 100644 index 0000000000000..bdcbba73e8793 --- /dev/null +++ b/azurerm/internal/services/datafactory/data_factory_linked_service_odata_resource_test.go @@ -0,0 +1,237 @@ +package datafactory_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance/check" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +type LinkedServiceODataResource struct { +} + +func TestAccDataFactoryLinkedServiceOData_anon_auth(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_data_factory_linked_service_odata", "test") + r := LinkedServiceODataResource{} + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.anon_auth(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("url").Exists(), + check.That(data.ResourceName).Key("url").HasValue("https://services.odata.org/v4/TripPinServiceRW/People"), + ), + }, + data.ImportStep(), + }) +} + +func TestAccDataFactoryLinkedServiceOData_basic_auth(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_data_factory_linked_service_odata", "test") + r := LinkedServiceODataResource{} + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.basic_auth(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("url").Exists(), + check.That(data.ResourceName).Key("url").HasValue("https://services.odata.org/v4/TripPinServiceRW/People"), + check.That(data.ResourceName).Key("basic_authentication.username").Exists(), + check.That(data.ResourceName).Key("basic_authentication.username").HasValue("emma"), + check.That(data.ResourceName).Key("basic_authentication.password").Exists(), + check.That(data.ResourceName).Key("basic_authentication.password").HasValue("Ch4ngeM3!"), + ), + }, + data.ImportStep(), + }) +} + +func TestAccDataFactoryLinkedServiceOData_basic_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_data_factory_linked_service_odata", "test") + r := LinkedServiceWebResource{} + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.update1(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("parameters.%").HasValue("2"), + check.That(data.ResourceName).Key("annotations.#").HasValue("3"), + check.That(data.ResourceName).Key("additional_properties.%").HasValue("2"), + check.That(data.ResourceName).Key("description").HasValue("test description"), + check.That(data.ResourceName).Key("url").HasValue("https://services.odata.org/v4/TripPinServiceRW/"), + ), + }, + data.ImportStep(), + { + Config: r.update2(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("parameters.%").HasValue("3"), + check.That(data.ResourceName).Key("annotations.#").HasValue("2"), + check.That(data.ResourceName).Key("additional_properties.%").HasValue("1"), + check.That(data.ResourceName).Key("description").HasValue("Test Description 2"), + check.That(data.ResourceName).Key("url").HasValue("https://services.odata.org/v4/TripPinServiceRW/People"), + ), + }, + data.ImportStep(), + }) +} + +func (t LinkedServiceODataResource) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) { + id, err := azure.ParseAzureResourceID(state.ID) + if err != nil { + return nil, err + } + resourceGroup := id.ResourceGroup + dataFactoryName := id.Path["factories"] + name := id.Path["linkedservices"] + + resp, err := clients.DataFactory.LinkedServiceClient.Get(ctx, resourceGroup, dataFactoryName, name, "") + if err != nil { + return nil, fmt.Errorf("reading Data Factory Linked Service OData (%s): %+v", id, err) + } + + return utils.Bool(resp.ID != nil), nil +} + +func (LinkedServiceODataResource) anon_auth(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-df-%d" + location = "%s" +} + +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_odata" "test" { + name = "acctestlsodata%d" + resource_group_name = azurerm_resource_group.test.name + data_factory_name = azurerm_data_factory.test.name + url = "https://services.odata.org/v4/TripPinServiceRW/People" +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) +} + +func (LinkedServiceODataResource) basic_auth(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-df-%d" + location = "%s" +} + +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_odata" "test" { + name = "acctestlsodata%d" + resource_group_name = azurerm_resource_group.test.name + data_factory_name = azurerm_data_factory.test.name + url = "https://services.odata.org/v4/TripPinServiceRW/People" + basic_authentication { + username = "emma" + password = "Ch4ngeM3!" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) +} + +func (LinkedServiceODataResource) update1(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-df-%d" + location = "%s" +} + +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_odata" "test" { + name = "acctestlsweb%d" + resource_group_name = azurerm_resource_group.test.name + data_factory_name = azurerm_data_factory.test.name + url = "https://services.odata.org/v4/TripPinServiceRW/" + annotations = ["test1", "test2", "test3"] + description = "test description" + + parameters = { + foo = "test1" + bar = "test2" + } + + additional_properties = { + foo = "test1" + bar = "test2" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) +} + +func (LinkedServiceODataResource) update2(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-df-%d" + location = "%s" +} + +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_odata" "test" { + name = "acctestlsweb%d" + resource_group_name = azurerm_resource_group.test.name + data_factory_name = azurerm_data_factory.test.name + url = "https://services.odata.org/v4/TripPinServiceRW/People" + annotations = ["test1", "test2"] + description = "Test Description 2" + + parameters = { + foo = "Test1" + bar = "Test2" + buzz = "Test3" + } + + additional_properties = { + foo = "Test1" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) +} diff --git a/azurerm/internal/services/datafactory/registration.go b/azurerm/internal/services/datafactory/registration.go index 28c5a444b5abb..8743895c717b9 100644 --- a/azurerm/internal/services/datafactory/registration.go +++ b/azurerm/internal/services/datafactory/registration.go @@ -53,6 +53,7 @@ func (r Registration) SupportedResources() map[string]*schema.Resource { "azurerm_data_factory_linked_service_data_lake_storage_gen2": resourceDataFactoryLinkedServiceDataLakeStorageGen2(), "azurerm_data_factory_linked_service_key_vault": resourceDataFactoryLinkedServiceKeyVault(), "azurerm_data_factory_linked_service_mysql": resourceDataFactoryLinkedServiceMySQL(), + "azurerm_data_factory_linked_service_odata": resourceArmDataFactoryLinkedServiceOData(), "azurerm_data_factory_linked_service_postgresql": resourceDataFactoryLinkedServicePostgreSQL(), "azurerm_data_factory_linked_service_sftp": resourceDataFactoryLinkedServiceSFTP(), "azurerm_data_factory_linked_service_snowflake": resourceDataFactoryLinkedServiceSnowflake(), diff --git a/website/docs/r/data_factory_linked_service_odata.html.markdown b/website/docs/r/data_factory_linked_service_odata.html.markdown new file mode 100644 index 0000000000000..2c4feba0bf1b2 --- /dev/null +++ b/website/docs/r/data_factory_linked_service_odata.html.markdown @@ -0,0 +1,102 @@ +--- +subcategory: "Data Factory" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_data_factory_linked_service_odata" +description: |- + Manages a Linked Service (connection) between a Database and Azure Data Factory through OData protocol. +--- + +# azurerm_data_factory_linked_service_odata + +Manages a Linked Service (connection) between a Database and Azure Data Factory through OData protocol. + +~> **Note:** All arguments including the connection_string will be stored in the raw state as plain-text. [Read more about sensitive data in state](/docs/state/sensitive-data.html). + +## Example Usage + +```hcl +resource "azurerm_resource_group" "example" { + name = "example-resources" + location = "West Europe" +} + +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_odata" "anonymous" { + name = "anonymous" + resource_group_name = azurerm_resource_group.example.name + data_factory_name = azurerm_data_factory.example.name + url = "https://services.odata.org/v4/TripPinServiceRW/People" +} + +resource "azurerm_data_factory_linked_service_odata" "basic_auth" { + name = "basic_auth" + resource_group_name = azurerm_resource_group.example.name + data_factory_name = azurerm_data_factory.example.name + url = "https://services.odata.org/v4/TripPinServiceRW/People" + basic_authentication { + username = "emma" + password = "Ch4ngeM3!" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) Specifies the name of the Data Factory Linked Service OData. Changing this forces a new resource to be created. Must be unique within a data + factory. See the [Microsoft documentation](https://docs.microsoft.com/en-us/azure/data-factory/naming-rules) for all restrictions. + +* `resource_group_name` - (Required) The name of the resource group in which to create the Data Factory Linked Service OData. Changing this forces a new resource + +* `data_factory_name` - (Required) The Data Factory name in which to associate the Linked Service with. Changing this forces a new resource. + +* `url` - (Required) The URL of the OData service endpoint. + +* `basic_authentication` - (Optional) A `basic_authentication` block as defined below. + +* `description` - (Optional) The description for the Data Factory Linked Service OData. + +* `integration_runtime_name` - (Optional) The integration runtime reference to associate with the Data Factory Linked Service OData. + +* `annotations` - (Optional) List of tags that can be used for describing the Data Factory Linked Service OData. + +* `parameters` - (Optional) A map of parameters to associate with the Data Factory Linked Service OData. + +* `additional_properties` - (Optional) A map of additional properties to associate with the Data Factory Linked Service OData. + +--- + +A `basic_authentication` block supports the following: + +* `username` - (Required) The username which can be used to authenticate to the OData endpoint. + +* `password` - (Required) The password associated with the username, which can be used to authenticate to the OData endpoint. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the Data Factory OData Linked Service. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions: + +* `create` - (Defaults to 30 minutes) Used when creating the Data Factory OData Linked Service. +* `update` - (Defaults to 30 minutes) Used when updating the Data Factory OData Linked Service. +* `read` - (Defaults to 5 minutes) Used when retrieving the Data Factory OData Linked Service. +* `delete` - (Defaults to 30 minutes) Used when deleting the Data Factory OData Linked Service. + +## Import + +Data Factory OData Linked Service's can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_data_factory_linked_service_odata.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/example/providers/Microsoft.DataFactory/factories/example/linkedservices/example +```