From 57deeb60dc4ae74f5f61674e28d8a08202d3cbaf Mon Sep 17 00:00:00 2001 From: Alex Wilcox Date: Sun, 8 Dec 2024 21:35:29 +0000 Subject: [PATCH] Add DS records --- .../services/dns/dns_ds_record_data_source.go | 145 ++++++++ .../dns/dns_ds_record_data_source_test.go | 44 +++ .../services/dns/dns_ds_record_resource.go | 314 ++++++++++++++++++ .../dns/dns_ds_record_resource_test.go | 305 +++++++++++++++++ internal/services/dns/registration.go | 8 +- website/docs/d/dns_ds_record.html.markdown | 62 ++++ website/docs/r/dns_ds_record.html.markdown | 98 ++++++ 7 files changed, 974 insertions(+), 2 deletions(-) create mode 100644 internal/services/dns/dns_ds_record_data_source.go create mode 100644 internal/services/dns/dns_ds_record_data_source_test.go create mode 100644 internal/services/dns/dns_ds_record_resource.go create mode 100644 internal/services/dns/dns_ds_record_resource_test.go create mode 100644 website/docs/d/dns_ds_record.html.markdown create mode 100644 website/docs/r/dns_ds_record.html.markdown diff --git a/internal/services/dns/dns_ds_record_data_source.go b/internal/services/dns/dns_ds_record_data_source.go new file mode 100644 index 000000000000..5c4f7dee8083 --- /dev/null +++ b/internal/services/dns/dns_ds_record_data_source.go @@ -0,0 +1,145 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dns + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2023-07-01-preview/recordsets" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2023-07-01-preview/zones" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" +) + +var _ sdk.DataSource = DnsDSRecordDataResource{} + +type DnsDSRecordDataResource struct{} + +func (DnsDSRecordDataResource) ModelObject() interface{} { + return &DnsDSRecordDataSourceModel{} +} + +func (d DnsDSRecordDataResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return validate.ValidateRecordTypeID(recordsets.RecordTypeDS) +} + +func (DnsDSRecordDataResource) ResourceType() string { + return "azurerm_dns_ds_record" +} + +type DnsDSRecordDataSourceModel struct { + Name string `tfschema:"name"` + ZoneId string `tfschema:"dns_zone_id"` + Ttl int64 `tfschema:"ttl"` + Record []DnsDSRecordResourceRecord `tfschema:"record"` + Tags map[string]string `tfschema:"tags"` + Fqdn string `tfschema:"fqdn"` +} + +func (DnsDSRecordDataResource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "dns_zone_id": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: zones.ValidateDnsZoneID, + }, + } +} + +func (DnsDSRecordDataResource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "record": { + Type: pluginsdk.TypeSet, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "algorithm": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "key_tag": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "digest_type": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "digest_value": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, + }, + + "ttl": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "fqdn": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "tags": commonschema.TagsDataSource(), + } +} + +func (DnsDSRecordDataResource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Dns.RecordSets + subscriptionId := metadata.Client.Account.SubscriptionId + + var state DnsDSRecordDataSourceModel + if err := metadata.Decode(&state); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + zoneId, err := zones.ParseDnsZoneID(state.ZoneId) + if err != nil { + return fmt.Errorf("parsing dns_zone_id: %+v", err) + } + id := recordsets.NewRecordTypeID(subscriptionId, zoneId.ResourceGroupName, zoneId.DnsZoneName, recordsets.RecordTypeDS, state.Name) + + resp, err := client.Get(ctx, id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("%s was not found", id) + } + return fmt.Errorf("retrieving %s: %+v", id, err) + } + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + state.Ttl = pointer.From(props.TTL) + state.Fqdn = pointer.From(props.Fqdn) + + state.Record = flattenDnsDSRecords(props.DSRecords) + + state.Tags = pointer.From(props.Metadata) + } + } + metadata.SetID(id) + + return metadata.Encode(&state) + }, + } +} diff --git a/internal/services/dns/dns_ds_record_data_source_test.go b/internal/services/dns/dns_ds_record_data_source_test.go new file mode 100644 index 000000000000..fa1e670daea9 --- /dev/null +++ b/internal/services/dns/dns_ds_record_data_source_test.go @@ -0,0 +1,44 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dns_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type DnsDSRecordDataSource struct{} + +func TestAccDataSourceDnsDSRecord_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_dns_ds_record", "test") + r := DnsDSRecordDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("name").Exists(), + check.That(data.ResourceName).Key("dns_zone_id").Exists(), + check.That(data.ResourceName).Key("record.#").HasValue("2"), + check.That(data.ResourceName).Key("ttl").Exists(), + check.That(data.ResourceName).Key("fqdn").Exists(), + check.That(data.ResourceName).Key("tags.%").HasValue("0"), + ), + }, + }) +} + +func (DnsDSRecordDataSource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +data "azurerm_dns_ds_record" "test" { + name = azurerm_dns_ds_record.test.name + dns_zone_id = azurerm_dns_zone.test.id +} +`, DnsDSRecordResource{}.basic(data)) +} diff --git a/internal/services/dns/dns_ds_record_resource.go b/internal/services/dns/dns_ds_record_resource.go new file mode 100644 index 000000000000..5a9a0aa1fce1 --- /dev/null +++ b/internal/services/dns/dns_ds_record_resource.go @@ -0,0 +1,314 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dns + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2023-07-01-preview/recordsets" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2023-07-01-preview/zones" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/dns/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" +) + +var ( + _ sdk.Resource = DnsDSRecordResource{} + _ sdk.ResourceWithUpdate = DnsDSRecordResource{} +) + +type DnsDSRecordResource struct{} + +func (DnsDSRecordResource) ModelObject() interface{} { + return &DnsDSRecordResourceModel{} +} + +func (DnsDSRecordResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return validate.ValidateRecordTypeID(recordsets.RecordTypeDS) +} + +func (DnsDSRecordResource) ResourceType() string { + return "azurerm_dns_ds_record" +} + +type DnsDSRecordResourceModel struct { + Name string `tfschema:"name"` + ZoneId string `tfschema:"dns_zone_id"` + Ttl int64 `tfschema:"ttl"` + Record []DnsDSRecordResourceRecord `tfschema:"record"` + Tags map[string]string `tfschema:"tags"` + Fqdn string `tfschema:"fqdn"` +} + +type DnsDSRecordResourceRecord struct { + Algorithm int64 `tfschema:"algorithm"` + KeyTag int64 `tfschema:"key_tag"` + DigestType int64 `tfschema:"digest_type"` + DigestValue string `tfschema:"digest_value"` +} + +func (DnsDSRecordResource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + }, + + "dns_zone_id": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: zones.ValidateDnsZoneID, + }, + + "record": { + Type: pluginsdk.TypeSet, + Required: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "algorithm": { + Type: pluginsdk.TypeInt, + Required: true, + }, + + "key_tag": { + Type: pluginsdk.TypeInt, + Required: true, + }, + + "digest_type": { + Type: pluginsdk.TypeInt, + Required: true, + }, + + "digest_value": { + Type: pluginsdk.TypeString, + Required: true, + }, + }, + }, + }, + + "ttl": { + Type: pluginsdk.TypeInt, + Required: true, + }, + + "tags": commonschema.Tags(), + } +} + +func (DnsDSRecordResource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "fqdn": { + Type: pluginsdk.TypeString, + Computed: true, + }, + } +} + +func (r DnsDSRecordResource) Create() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Dns.RecordSets + subscriptionId := metadata.Client.Account.SubscriptionId + + var model DnsDSRecordResourceModel + if err := metadata.Decode(&model); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + zoneId, err := zones.ParseDnsZoneID(model.ZoneId) + if err != nil { + return fmt.Errorf("parsing dns_zone_id: %+v", err) + } + id := recordsets.NewRecordTypeID(subscriptionId, zoneId.ResourceGroupName, zoneId.DnsZoneName, recordsets.RecordTypeDS, model.Name) + + existing, err := client.Get(ctx, id) + if err != nil { + if !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) + } + } + + if !response.WasNotFound(existing.HttpResponse) { + return metadata.ResourceRequiresImport(r.ResourceType(), id) + } + + parameters := recordsets.RecordSet{ + Name: pointer.To(model.Name), + Properties: &recordsets.RecordSetProperties{ + Metadata: pointer.To(model.Tags), + TTL: pointer.To(model.Ttl), + DSRecords: expandDnsDSRecords(model.Record), + }, + } + + if _, err := client.CreateOrUpdate(ctx, id, parameters, recordsets.DefaultCreateOrUpdateOperationOptions()); err != nil { + return fmt.Errorf("creating %s: %+v", id, err) + } + + metadata.SetID(id) + + return nil + }, + } +} + +func (DnsDSRecordResource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Dns.RecordSets + + state := DnsDSRecordResourceModel{} + + id, err := recordsets.ParseRecordTypeID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + resp, err := client.Get(ctx, *id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return metadata.MarkAsGone(id) + } + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + zoneId := zones.NewDnsZoneID(id.SubscriptionId, id.ResourceGroupName, id.DnsZoneName) + state.Name = id.RelativeRecordSetName + state.ZoneId = zoneId.ID() + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + state.Ttl = pointer.From(props.TTL) + state.Fqdn = pointer.From(props.Fqdn) + + state.Record = flattenDnsDSRecords(props.DSRecords) + + state.Tags = pointer.From(props.Metadata) + } + } + + return metadata.Encode(&state) + }, + } +} + +func (DnsDSRecordResource) Update() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Dns.RecordSets + + var model DnsDSRecordResourceModel + + if err := metadata.Decode(&model); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + id, err := recordsets.ParseRecordTypeID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + existing, err := client.Get(ctx, *id) + if err != nil { + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + if existing.Model == nil { + return fmt.Errorf("retrieving %s: `model` was nil", id) + } + + if existing.Model.Properties == nil { + return fmt.Errorf("retrieving %s: `properties` was nil", id) + } + + payload := *existing.Model + + if metadata.ResourceData.HasChange("record") { + payload.Properties.DSRecords = expandDnsDSRecords(model.Record) + } + + if metadata.ResourceData.HasChange("ttl") { + payload.Properties.TTL = pointer.To(model.Ttl) + } + + if metadata.ResourceData.HasChange("tags") { + payload.Properties.Metadata = pointer.To(model.Tags) + } + + if _, err := client.CreateOrUpdate(ctx, *id, payload, recordsets.DefaultCreateOrUpdateOperationOptions()); err != nil { + return fmt.Errorf("updating %s: %+v", id, err) + } + + return nil + }, + } +} + +func (DnsDSRecordResource) Delete() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Dns.RecordSets + + id, err := recordsets.ParseRecordTypeID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + if _, err := client.Delete(ctx, *id, recordsets.DefaultDeleteOperationOptions()); err != nil { + return fmt.Errorf("deleting %s: %+v", *id, err) + } + + return nil + }, + } +} + +func flattenDnsDSRecords(records *[]recordsets.DsRecord) []DnsDSRecordResourceRecord { + results := make([]DnsDSRecordResourceRecord, 0) + + if records != nil { + for _, record := range *records { + result := DnsDSRecordResourceRecord{ + Algorithm: pointer.From(record.Algorithm), + KeyTag: pointer.From(record.KeyTag), + } + + if record.Digest != nil { + result.DigestType = pointer.From(record.Digest.AlgorithmType) + result.DigestValue = pointer.From(record.Digest.Value) + } + results = append(results, result) + } + } + + return results +} + +func expandDnsDSRecords(d []DnsDSRecordResourceRecord) *[]recordsets.DsRecord { + records := make([]recordsets.DsRecord, 0) + + for _, v := range d { + records = append(records, recordsets.DsRecord{ + Algorithm: pointer.To(v.Algorithm), + KeyTag: pointer.To(v.KeyTag), + Digest: &recordsets.Digest{ + AlgorithmType: pointer.To(v.DigestType), + Value: pointer.To(v.DigestValue), + }, + }) + } + + return &records +} diff --git a/internal/services/dns/dns_ds_record_resource_test.go b/internal/services/dns/dns_ds_record_resource_test.go new file mode 100644 index 000000000000..39876908385a --- /dev/null +++ b/internal/services/dns/dns_ds_record_resource_test.go @@ -0,0 +1,305 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dns_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-sdk/resource-manager/dns/2023-07-01-preview/recordsets" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" +) + +type DnsDSRecordResource struct{} + +func TestAccDnsDSRecord_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_dns_ds_record", "test") + r := DnsDSRecordResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("fqdn").Exists(), + ), + }, + data.ImportStep(), + }) +} + +func TestAccDnsDSRecord_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_dns_ds_record", "test") + r := DnsDSRecordResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccDnsDSRecord_updateRecords(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_dns_ds_record", "test") + r := DnsDSRecordResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("record.#").HasValue("2"), + ), + }, + data.ImportStep(), + { + Config: r.updateRecords(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("record.#").HasValue("3"), + ), + }, + data.ImportStep()}) +} + +func TestAccDnsDSRecord_withTags(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_dns_ds_record", "test") + r := DnsDSRecordResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.withTags(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("tags.%").HasValue("2"), + ), + }, + data.ImportStep(), + { + Config: r.withTagsUpdate(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("tags.%").HasValue("1"), + ), + }, + data.ImportStep(), + }) +} + +func (DnsDSRecordResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := recordsets.ParseRecordTypeID(state.ID) + if err != nil { + return nil, err + } + + resp, err := clients.Dns.RecordSets.Get(ctx, *id) + if err != nil { + return nil, fmt.Errorf("retrieving %s: %+v", *id, err) + } + + return pointer.To(resp.Model != nil), nil +} + +func (DnsDSRecordResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_dns_zone" "test" { + name = "acctestzone%d.com" + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_dns_ds_record" "test" { + name = "myarecord%d" + dns_zone_id = azurerm_dns_zone.test.id + ttl = 300 + + record { + algorithm = 13 + key_tag = 28237 + digest_type = 2 + digest_value = "40F628643831D5EAF7D005D3237DE32F3F37AE6025C7891D202B0BAFA9924778" + } + + record { + algorithm = 13 + key_tag = 46872 + digest_type = 2 + digest_value = "2C0BAC20EB5C8C315694CBEB62E56C71CDC0069D058A8B80992E6499D91DD247" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) +} + +func (r DnsDSRecordResource) requiresImport(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_dns_ds_record" "import" { + name = azurerm_dns_ds_record.test.name + dns_zone_id = azurerm_dns_zone.test.id + ttl = 300 + + record { + algorithm = 13 + key_tag = 28237 + digest_type = 2 + digest_value = "40F628643831D5EAF7D005D3237DE32F3F37AE6025C7891D202B0BAFA9924778" + } + + record { + algorithm = 13 + key_tag = 46872 + digest_type = 2 + digest_value = "2C0BAC20EB5C8C315694CBEB62E56C71CDC0069D058A8B80992E6499D91DD247" + } +} +`, r.basic(data)) +} + +func (DnsDSRecordResource) updateRecords(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_dns_zone" "test" { + name = "acctestzone%d.com" + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_dns_ds_record" "test" { + name = "myarecord%d" + dns_zone_id = azurerm_dns_zone.test.id + ttl = 300 + + record { + algorithm = 13 + key_tag = 28237 + digest_type = 2 + digest_value = "40F628643831D5EAF7D005D3237DE32F3F37AE6025C7891D202B0BAFA9924778" + } + + record { + algorithm = 13 + key_tag = 46872 + digest_type = 2 + digest_value = "2C0BAC20EB5C8C315694CBEB62E56C71CDC0069D058A8B80992E6499D91DD247" + } + + record { + algorithm = 13 + key_tag = 20795 + digest_type = 2 + digest_value = "55E20DB8044B0C6190A925598F08F8146C9A0D4F668F8CA5A7276EB54064C5E3" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) +} + +func (DnsDSRecordResource) withTags(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_dns_zone" "test" { + name = "acctestzone%d.com" + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_dns_ds_record" "test" { + name = "myarecord%d" + dns_zone_id = azurerm_dns_zone.test.id + ttl = 300 + + record { + algorithm = 13 + key_tag = 28237 + digest_type = 2 + digest_value = "40F628643831D5EAF7D005D3237DE32F3F37AE6025C7891D202B0BAFA9924778" + } + + record { + algorithm = 13 + key_tag = 46872 + digest_type = 2 + digest_value = "2C0BAC20EB5C8C315694CBEB62E56C71CDC0069D058A8B80992E6499D91DD247" + } + + tags = { + environment = "Production" + cost_center = "MSFT" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) +} + +func (DnsDSRecordResource) withTagsUpdate(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_dns_zone" "test" { + name = "acctestzone%d.com" + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_dns_ds_record" "test" { + name = "myarecord%d" + dns_zone_id = azurerm_dns_zone.test.id + ttl = 300 + + record { + algorithm = 13 + key_tag = 28237 + digest_type = 2 + digest_value = "40F628643831D5EAF7D005D3237DE32F3F37AE6025C7891D202B0BAFA9924778" + } + + record { + algorithm = 13 + key_tag = 46872 + digest_type = 2 + digest_value = "2C0BAC20EB5C8C315694CBEB62E56C71CDC0069D058A8B80992E6499D91DD247" + } + + tags = { + environment = "staging" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) +} diff --git a/internal/services/dns/registration.go b/internal/services/dns/registration.go index 6716712ba7f2..7055cbed6901 100644 --- a/internal/services/dns/registration.go +++ b/internal/services/dns/registration.go @@ -66,10 +66,14 @@ func (r Registration) SupportedResources() map[string]*pluginsdk.Resource { // DataSources returns a list of Data Sources supported by this Service func (r Registration) DataSources() []sdk.DataSource { - return []sdk.DataSource{} + return []sdk.DataSource{ + DnsDSRecordDataResource{}, + } } // Resources returns a list of Resources supported by this Service func (r Registration) Resources() []sdk.Resource { - return []sdk.Resource{} + return []sdk.Resource{ + DnsDSRecordResource{}, + } } diff --git a/website/docs/d/dns_ds_record.html.markdown b/website/docs/d/dns_ds_record.html.markdown new file mode 100644 index 000000000000..e1de195e8c7b --- /dev/null +++ b/website/docs/d/dns_ds_record.html.markdown @@ -0,0 +1,62 @@ +--- +subcategory: "DNS" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_dns_ds_record" +description: |- + Gets information about an existing DNS DS Record. +--- + +# Data Source: azurerm_dns_ds_record + +Use this data source to access information about an existing DNS DS Record within Azure DNS. + +~> **Note:** [The Azure DNS API has a throttle limit of 500 read (GET) operations per 5 minutes](https://docs.microsoft.com/azure/azure-resource-manager/management/request-limits-and-throttling#network-throttling) - whilst the default read timeouts will work for most cases - in larger configurations you may need to set a larger [read timeout](https://www.terraform.io/language/resources/syntax#operation-timeouts) then the default 5min. Although, we'd generally recommend that you split the resources out into smaller Terraform configurations to avoid the problem entirely. + +## Example Usage + +```hcl +data "azurerm_dns_ds_record" "example" { + name = "test" + dns_zone_id = data.azurerm_dns_zone.example.id +} + +output "dns_ds_record_id" { + value = data.azurerm_dns_ds_record.example.id +} +``` + +## Argument Reference + +* `name` - The name of the DNS DS Record. + +* `dns_zone_id` - Specifies the DNS Zone ID where the resource exists. + +## Attributes Reference + +* `id` - The DNS DS Record ID. + +* `fqdn` - The FQDN of the DNS DS Record. + +* `ttl` - The Time To Live (TTL) of the DNS record in seconds. + +* `record` - A list of values that make up the DS record. Each `record` block supports fields documented below. + +* `tags` - A mapping of tags assigned to the resource. + +--- + +The `record` block supports: + +* `algorithm` - Identifies the algorithm used to produce a legitimate signature. + +* `key_tag` - Contains the tag value of the DNSKEY Resource Record that validates this signature. + +* `digest_type` - Identifies the algorithm used to construct the digest. + +* `digest_value` - A cryptographic hash value of the referenced DNSKEY Record. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/language/resources/syntax#operation-timeouts) for certain actions: + +* `read` - (Defaults to 5 minutes) Used when retrieving the DNS DS Record. diff --git a/website/docs/r/dns_ds_record.html.markdown b/website/docs/r/dns_ds_record.html.markdown new file mode 100644 index 000000000000..3ca8dc4fb7d2 --- /dev/null +++ b/website/docs/r/dns_ds_record.html.markdown @@ -0,0 +1,98 @@ +--- +subcategory: "DNS" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_dns_ds_record" +description: |- + Manages a DNS DS Record. +--- + +# azurerm_dns_ds_record + +Enables you to manage DNS DS Records within Azure DNS. + +~> **Note:** [The Azure DNS API has a throttle limit of 500 read (GET) operations per 5 minutes](https://docs.microsoft.com/azure/azure-resource-manager/management/request-limits-and-throttling#network-throttling) - whilst the default read timeouts will work for most cases - in larger configurations you may need to set a larger [read timeout](https://www.terraform.io/language/resources/syntax#operation-timeouts) then the default 5min. Although, we'd generally recommend that you split the resources out into smaller Terraform configurations to avoid the problem entirely. + +## Example Usage + +```hcl +resource "azurerm_resource_group" "example" { + name = "example-resources" + location = "West Europe" +} + +resource "azurerm_dns_zone" "example" { + name = "mydomain.com" + resource_group_name = azurerm_resource_group.example.name +} + +resource "azurerm_dns_ds_record" "example" { + name = "test" + dns_zone_id = azurerm_dns_zone.example.id + ttl = 300 + + record { + algorithm = 13 + key_tag = 28237 + digest_type = 2 + digest_value = "40F628643831D5EAF7D005D3237DE32F3F37AE6025C7891D202B0BAFA9924778" + } + + tags = { + Environment = "Production" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +- `name` - (Required) The name of the DNS DS Record. Changing this forces a new resource to be created. + +- `dns_zone_id` - (Required) Specifies the DNS Zone ID where the resource exists. Changing this forces a new resource to be created. + +- `ttl` - (Required) The Time To Live (TTL) of the DNS record in seconds. + +- `record` - (Required) A list of values that make up the DS record. Each `record` block supports fields documented below. + +- `tags` - (Optional) A mapping of tags to assign to the resource. + +--- + +The `record` block supports: + +- `algorithm` - (Required) Identifies the algorithm used to produce a legitimate signature. + +- `key_tag` - (Required) Contains the tag value of the DNSKEY Resource Record that validates this signature. + +- `digest_type` - (Required) Identifies the algorithm used to construct the digest. + +- `digest_value` - (Required) A cryptographic hash value of the referenced DNSKEY Record. + +## Attributes Reference + +In addition to the Arguments listed above - the following Attributes are exported: + +- `id` - The DNS DS Record ID. + +- `fqdn` - The FQDN of the DNS DS Record. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/language/resources/syntax#operation-timeouts) for certain actions: + +- `create` - (Defaults to 30 minutes) Used when creating the DNS DS Record. + +- `update` - (Defaults to 30 minutes) Used when updating the DNS DS Record. + +- `read` - (Defaults to 5 minutes) Used when retrieving the DNS DS Record. + +- `delete` - (Defaults to 30 minutes) Used when deleting the DNS DS Record. + +## Import + +DS records can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_dns_ds_record.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mygroup1/providers/Microsoft.Network/dnsZones/zone1/DS/myrecord1 +```