diff --git a/azurerm/internal/services/firewall/firewall_policy_resource.go b/azurerm/internal/services/firewall/firewall_policy_resource.go index 086d1f94d2c4..4b6c5b1cb977 100644 --- a/azurerm/internal/services/firewall/firewall_policy_resource.go +++ b/azurerm/internal/services/firewall/firewall_policy_resource.go @@ -290,25 +290,6 @@ func resourceFirewallPolicy() *pluginsdk.Resource { }, }, - "tls_certificate": { - Type: pluginsdk.TypeList, - Optional: true, - MaxItems: 1, - MinItems: 1, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - "key_vault_secret_id": { - Type: pluginsdk.TypeString, - Required: true, - }, - "name": { - Type: pluginsdk.TypeString, - Required: true, - }, - }, - }, - }, - "child_policies": { Type: pluginsdk.TypeList, Computed: true, @@ -365,7 +346,6 @@ func resourceFirewallPolicyCreateUpdate(d *pluginsdk.ResourceData, meta interfac ThreatIntelWhitelist: expandFirewallPolicyThreatIntelWhitelist(d.Get("threat_intelligence_allowlist").([]interface{})), DNSSettings: expandFirewallPolicyDNSSetting(d.Get("dns").([]interface{})), IntrusionDetection: expandFirewallPolicyIntrusionDetection(d.Get("intrusion_detection").([]interface{})), - TransportSecurity: expandFirewallPolicyTransportSecurity(d.Get("tls_certificate").([]interface{})), }, Identity: expandFirewallPolicyIdentity(d.Get("identity").([]interface{})), Location: utils.String(location.Normalize(d.Get("location").(string))), @@ -450,10 +430,6 @@ func resourceFirewallPolicyRead(d *pluginsdk.ResourceData, meta interface{}) err return fmt.Errorf(`setting "intrusion_detection": %+v`, err) } - if err := d.Set("tls_certificate", flattenFirewallPolicyTransportSecurity(prop.TransportSecurity)); err != nil { - return fmt.Errorf(`setting "tls_certificate": %+v`, err) - } - if err := d.Set("child_policies", flattenNetworkSubResourceID(prop.ChildPolicies)); err != nil { return fmt.Errorf(`setting "child_policies": %+v`, err) } @@ -566,21 +542,6 @@ func expandFirewallPolicyIntrusionDetection(input []interface{}) *network.Firewa } -func expandFirewallPolicyTransportSecurity(input []interface{}) *network.FirewallPolicyTransportSecurity { - if len(input) == 0 || input[0] == nil { - return nil - } - - raw := input[0].(map[string]interface{}) - - return &network.FirewallPolicyTransportSecurity{ - CertificateAuthority: &network.FirewallPolicyCertificateAuthority{ - KeyVaultSecretID: utils.String(raw["key_vault_secret_id"].(string)), - Name: utils.String(raw["name"].(string)), - }, - } -} - func expandFirewallPolicyIdentity(input []interface{}) *network.ManagedServiceIdentity { if len(input) == 0 { return nil @@ -641,19 +602,6 @@ func flattenFirewallPolicyIntrusionDetection(input *network.FirewallPolicyIntrus } } -func flattenFirewallPolicyTransportSecurity(input *network.FirewallPolicyTransportSecurity) []interface{} { - if input == nil { - return []interface{}{} - } - - return []interface{}{ - map[string]interface{}{ - "key_vault_secret_id": input.CertificateAuthority.KeyVaultSecretID, - "name": input.CertificateAuthority.Name, - }, - } -} - func flattenFirewallPolicyIdentity(identity *network.ManagedServiceIdentity) []interface{} { if identity == nil { return []interface{}{} diff --git a/azurerm/internal/services/firewall/firewall_policy_resource_test.go b/azurerm/internal/services/firewall/firewall_policy_resource_test.go index de795484faea..349a03e163f2 100644 --- a/azurerm/internal/services/firewall/firewall_policy_resource_test.go +++ b/azurerm/internal/services/firewall/firewall_policy_resource_test.go @@ -271,10 +271,6 @@ resource "azurerm_firewall_policy" "test" { identity { type = "SystemAssigned" } - tls_certificate { - key_vault_secret_id = azurerm_key_vault_certificate.test.secret_id - name = azurerm_key_vault_certificate.test.name - } tags = { env = "Test" } diff --git a/azurerm/internal/services/firewall/firewall_policy_tls_certificate.go b/azurerm/internal/services/firewall/firewall_policy_tls_certificate.go new file mode 100644 index 000000000000..8956c38457e6 --- /dev/null +++ b/azurerm/internal/services/firewall/firewall_policy_tls_certificate.go @@ -0,0 +1,221 @@ +package firewall + +import ( + "fmt" + "log" + "time" + + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2020-11-01/network" + "github.com/hashicorp/go-azure-helpers/response" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/location" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/firewall/parse" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/firewall/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/pluginsdk" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +const azureFirewallPolicyTLSCertificateResourceName = "azurerm_firewall_policy_tls_certificate" + +func resourceFirewallPolicyTLSCertificate() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Create: resourceFirewallPolicyTLSCertificateCreateUpdate, + Read: resourceFirewallPolicyTLSCertificateRead, + Update: resourceFirewallPolicyTLSCertificateCreateUpdate, + Delete: resourceFirewallPolicyTLSCertificateDelete, + + Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { + _, err := parse.FirewallPolicyID(id) + return err + }), + + Timeouts: &pluginsdk.ResourceTimeout{ + Create: pluginsdk.DefaultTimeout(30 * time.Minute), + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + Update: pluginsdk.DefaultTimeout(30 * time.Minute), + Delete: pluginsdk.DefaultTimeout(30 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "firewall_policy_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.FirewallPolicyName(), + }, + + "tls_certificate": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + MinItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "key_vault_secret_id": { + Type: pluginsdk.TypeString, + Required: true, + }, + "name": { + Type: pluginsdk.TypeString, + Required: true, + }, + }, + }, + }, + }, + } +} + +func resourceFirewallPolicyTLSCertificateCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Firewall.FirewallPolicyClient + ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) + defer cancel() + + firewall_policy_id, err := parse.FirewallPolicyID(d.Get("firewall_policy_id").(string)) + + resp, err := client.Get(ctx, firewall_policy_id.ResourceGroup, firewall_policy_id.Name, "") + if err != nil { + if !utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("checking for existing Firewall Policy %q (Resource Group %q): %+v", + firewall_policy_id.Name, firewall_policy_id.ResourceGroup, err) + } + } + + resp.TransportSecurity = expandFirewallPolicyTransportSecurity(d.Get("tls_certificate").([]interface{})) + + d.SetId(*resp.ID) + + return resourceFirewallPolicyTLSCertificateRead(d, meta) +} + +func resourceFirewallPolicyTLSCertificateRead(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Firewall.FirewallPolicyClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.FirewallPolicyID(d.Id()) + if err != nil { + return err + } + + resp, err := client.Get(ctx, id.ResourceGroup, id.Name, "") + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[DEBUG] Firewall Policy %q was not found in Resource Group %q - removing from state!", id.Name, id.ResourceGroup) + d.SetId("") + return nil + } + + return fmt.Errorf("retrieving Firewall Policy %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + } + + d.Set("name", id.Name) + d.Set("resource_group_name", id.ResourceGroup) + d.Set("location", location.NormalizeNilable(resp.Location)) + + if prop := resp.FirewallPolicyPropertiesFormat; prop != nil { + basePolicyID := "" + if resp.BasePolicy != nil && resp.BasePolicy.ID != nil { + basePolicyID = *resp.BasePolicy.ID + } + d.Set("base_policy_id", basePolicyID) + + d.Set("threat_intelligence_mode", string(prop.ThreatIntelMode)) + + if sku := prop.Sku; sku != nil { + d.Set("sku", string(sku.Tier)) + } + + if err := d.Set("threat_intelligence_allowlist", flattenFirewallPolicyThreatIntelWhitelist(resp.ThreatIntelWhitelist)); err != nil { + return fmt.Errorf(`setting "threat_intelligence_allowlist": %+v`, err) + } + + if err := d.Set("dns", flattenFirewallPolicyDNSSetting(prop.DNSSettings)); err != nil { + return fmt.Errorf(`setting "dns": %+v`, err) + } + + if err := d.Set("intrusion_detection", flattenFirewallPolicyIntrusionDetection(resp.IntrusionDetection)); err != nil { + return fmt.Errorf(`setting "intrusion_detection": %+v`, err) + } + + if err := d.Set("tls_certificate", flattenFirewallPolicyTransportSecurity(prop.TransportSecurity)); err != nil { + return fmt.Errorf(`setting "tls_certificate": %+v`, err) + } + + if err := d.Set("child_policies", flattenNetworkSubResourceID(prop.ChildPolicies)); err != nil { + return fmt.Errorf(`setting "child_policies": %+v`, err) + } + + if err := d.Set("firewalls", flattenNetworkSubResourceID(prop.Firewalls)); err != nil { + return fmt.Errorf(`setting "firewalls": %+v`, err) + } + + if err := d.Set("rule_collection_groups", flattenNetworkSubResourceID(prop.RuleCollectionGroups)); err != nil { + return fmt.Errorf(`setting "rule_collection_groups": %+v`, err) + } + } + + if err := d.Set("identity", flattenFirewallPolicyIdentity(resp.Identity)); err != nil { + return fmt.Errorf("flattening identity on Firewall Policy %q (Resource Group %q): %+v", + id.Name, id.ResourceGroup, err) + } + + return tags.FlattenAndSet(d, resp.Tags) +} + +func resourceFirewallPolicyTLSCertificateDelete(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Firewall.FirewallPolicyClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.FirewallPolicyID(d.Id()) + if err != nil { + return err + } + + locks.ByName(id.Name, azureFirewallPolicyTLSCertificateResourceName) + defer locks.UnlockByName(id.Name, azureFirewallPolicyTLSCertificateResourceName) + + future, err := client.Delete(ctx, id.ResourceGroup, id.Name) + if err != nil { + return fmt.Errorf("deleting Firewall Policy %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + } + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + if !response.WasNotFound(future.Response()) { + return fmt.Errorf("waiting for deleting Firewall Policy %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + } + } + + return nil +} + +func expandFirewallPolicyTransportSecurity(input []interface{}) *network.FirewallPolicyTransportSecurity { + if len(input) == 0 || input[0] == nil { + return nil + } + + raw := input[0].(map[string]interface{}) + + return &network.FirewallPolicyTransportSecurity{ + CertificateAuthority: &network.FirewallPolicyCertificateAuthority{ + KeyVaultSecretID: utils.String(raw["key_vault_secret_id"].(string)), + Name: utils.String(raw["name"].(string)), + }, + } +} + +func flattenFirewallPolicyTransportSecurity(input *network.FirewallPolicyTransportSecurity) []interface{} { + if input == nil { + return []interface{}{} + } + + return []interface{}{ + map[string]interface{}{ + "key_vault_secret_id": input.CertificateAuthority.KeyVaultSecretID, + "name": input.CertificateAuthority.Name, + }, + } +} diff --git a/azurerm/internal/services/firewall/firewall_policy_tls_certificate_test.go b/azurerm/internal/services/firewall/firewall_policy_tls_certificate_test.go new file mode 100644 index 000000000000..f27199e1f340 --- /dev/null +++ b/azurerm/internal/services/firewall/firewall_policy_tls_certificate_test.go @@ -0,0 +1,298 @@ +package firewall_test + +import ( + "context" + "fmt" + "testing" + + "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/internal/services/firewall/parse" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/pluginsdk" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +type FirewallPolicyTLSCertificateResource struct { +} + +func TestAccFirewallPolicyTLSCertificate_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_firewall_policy", "test") + r := FirewallPolicyTLSCertificateResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccFirewallPolicyTLSCertificate_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_firewall_policy", "test") + r := FirewallPolicyTLSCertificateResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccFirewallPolicyTLSCertificate_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_firewall_policy", "test") + r := FirewallPolicyTLSCertificateResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func (FirewallPolicyTLSCertificateResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + var id, err = parse.FirewallPolicyID(state.ID) + if err != nil { + return nil, err + } + + resp, err := clients.Firewall.FirewallPolicyClient.Get(ctx, id.ResourceGroup, id.Name, "") + if err != nil { + return nil, fmt.Errorf("retrieving %s: %v", id.String(), err) + } + + return utils.Bool(resp.FirewallPolicyPropertiesFormat != nil), nil +} + +func (FirewallPolicyTLSCertificateResource) basic(data acceptance.TestData) string { + r := FirewallPolicyTLSCertificateResource{} + template := r.template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_firewall_policy" "test" { + name = "acctest-networkfw-Policy-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + threat_intelligence_mode = "Off" + threat_intelligence_allowlist { + ip_addresses = ["1.1.1.1", "2.2.2.2"] + fqdns = ["foo.com", "bar.com"] + } + dns { + servers = ["1.1.1.1", "2.2.2.2"] + proxy_enabled = true + } + intrusion_detection { + mode = "Alert" + signature_overrides { + state = "Alert" + id = "1" + } + traffic_bypass { + name = "Name bypass traffic settings" + description = "Description bypass traffic settings" + protocol = "ANY" + destination_addresses = ["*"] + destination_ports = ["*"] + source_ip_groups = ["*"] + destination_ip_groups = ["*"] + } + } + identity { + type = "SystemAssigned" + } + tls_certificate { + key_vault_secret_id = azurerm_key_vault_certificate.test.secret_id + name = azurerm_key_vault_certificate.test.name + } + tags = { + env = "Test" + } +} +`, template, data.RandomInteger) +} + +func (FirewallPolicyTLSCertificateResource) requiresImport(data acceptance.TestData) string { + r := FirewallPolicyTLSCertificateResource{} + template := r.basic(data) + return fmt.Sprintf(` +%s + +resource "azurerm_firewall_policy" "import" { + name = azurerm_firewall_policy.test.name + resource_group_name = azurerm_firewall_policy.test.resource_group_name + location = azurerm_firewall_policy.test.location +} +`, template) +} + +func (FirewallPolicyTLSCertificateResource) inherit(data acceptance.TestData) string { + r := FirewallPolicyTLSCertificateResource{} + template := r.template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_firewall_policy" "test-parent" { + name = "acctest-networkfw-Policy-%d-parent" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location +} + +resource "azurerm_firewall_policy" "test" { + name = "acctest-networkfw-Policy-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + base_policy_id = azurerm_firewall_policy.test-parent.id + threat_intelligence_allowlist { + ip_addresses = ["1.1.1.1", "2.2.2.2"] + fqdns = ["foo.com", "bar.com"] + } + dns { + servers = ["1.1.1.1", "2.2.2.2"] + proxy_enabled = true + } + tags = { + env = "Test" + } +} +`, template, data.RandomInteger, data.RandomInteger) +} + +func (FirewallPolicyTLSCertificateResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +data "azurerm_client_config" "current" { +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-networkfw-%d" + location = "%s" +} + +resource "azurerm_key_vault" "test" { + name = "tlskv%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + enabled_for_disk_encryption = true + enabled_for_deployment = true + enabled_for_template_deployment = true + tenant_id = data.azurerm_client_config.current.tenant_id + + sku_name = "standard" +} + +resource "azurerm_key_vault_access_policy" "test" { + key_vault_id = azurerm_key_vault.test.id + application_id = data.azurerm_client_config.current.client_id + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = data.azurerm_client_config.current.object_id + + key_permissions = [ + "backup", + "create", + "delete", + "get", + "import", + "list", + "purge", + "recover", + "restore", + "update" + ] + + certificate_permissions = [ + "backup", + "create", + "get", + "list", + "import", + "purge", + "delete", + "recover", + ] + + secret_permissions = [ + "get", + "list", + "set", + "purge", + "delete", + "recover" + ] + +} + +resource "azurerm_key_vault_certificate" "test" { + name = "AzureFirewallPolicyTLSCertificateCertificate" + key_vault_id = azurerm_key_vault.test.id + + certificate { + contents = filebase64("testdata/cert_key.pem") + } + + certificate_policy { + issuer_parameters { + name = "Self" + } + key_properties { + exportable = true + key_size = 2048 + key_type = "RSA" + reuse_key = true + } + secret_properties { + content_type = "application/x-pem-file" + } + x509_certificate_properties { + # Server Authentication = 1.3.6.1.5.5.7.3.1 + # Client Authentication = 1.3.6.1.5.5.7.3.2 + extended_key_usage = ["1.3.6.1.5.5.7.3.1"] + key_usage = [ + "cRLSign", + "dataEncipherment", + "digitalSignature", + "keyAgreement", + "keyCertSign", + "keyEncipherment", + ] + subject_alternative_names { + dns_names = ["api.pluginsdk.io"] + } + subject = "CN=api.pluginsdk.io" + validity_in_months = 1 + } + } + +} + +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +}