diff --git a/internal/services/mssql/client/client.go b/internal/services/mssql/client/client.go index fadade3a2650..3c56dd34da4c 100644 --- a/internal/services/mssql/client/client.go +++ b/internal/services/mssql/client/client.go @@ -23,6 +23,7 @@ type Client struct { LongTermRetentionPoliciesClient *sql.LongTermRetentionPoliciesClient ManagedDatabasesClient *sql.ManagedDatabasesClient ManagedInstancesClient *sql.ManagedInstancesClient + ManagedInstanceVulnerabilityAssessmentsClient *sql.ManagedInstanceVulnerabilityAssessmentsClient OutboundFirewallRulesClient *sql.OutboundFirewallRulesClient ManagedInstanceAdministratorsClient *sql.ManagedInstanceAdministratorsClient ManagedInstanceAzureADOnlyAuthenticationsClient *sql.ManagedInstanceAzureADOnlyAuthenticationsClient @@ -96,6 +97,9 @@ func NewClient(o *common.ClientOptions) *Client { managedInstanceAzureADOnlyAuthenticationsClient := sql.NewManagedInstanceAzureADOnlyAuthenticationsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&managedInstanceAzureADOnlyAuthenticationsClient.Client, o.ResourceManagerAuthorizer) + managedInstanceVulnerabilityAssessmentsClient := sql.NewManagedInstanceVulnerabilityAssessmentsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&managedInstanceVulnerabilityAssessmentsClient.Client, o.ResourceManagerAuthorizer) + outboundFirewallRulesClient := sql.NewOutboundFirewallRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&outboundFirewallRulesClient.Client, o.ResourceManagerAuthorizer) @@ -157,6 +161,7 @@ func NewClient(o *common.ClientOptions) *Client { ManagedInstanceAdministratorsClient: &managedInstancesAdministratorsClient, ManagedInstanceAzureADOnlyAuthenticationsClient: &managedInstanceAzureADOnlyAuthenticationsClient, ManagedInstancesClient: &managedInstancesClient, + ManagedInstanceVulnerabilityAssessmentsClient: &managedInstanceVulnerabilityAssessmentsClient, OutboundFirewallRulesClient: &outboundFirewallRulesClient, ReplicationLinksClient: &replicationLinksClient, RestorableDroppedDatabasesClient: &restorableDroppedDatabasesClient, diff --git a/internal/services/mssql/mssql_managed_instance_vulnerability_assessment_resource.go b/internal/services/mssql/mssql_managed_instance_vulnerability_assessment_resource.go new file mode 100644 index 000000000000..83f277a853ab --- /dev/null +++ b/internal/services/mssql/mssql_managed_instance_vulnerability_assessment_resource.go @@ -0,0 +1,180 @@ +package mssql + +import ( + "fmt" + "log" + "time" + + "github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/v5.0/sql" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/mssql/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/mssql/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +func resourceMsSqlManagedInstanceVulnerabilityAssessment() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Create: resourceMsSqlManagedInstanceVulnerabilityAssessmentCreateUpdate, + Read: resourceMsSqlManagedInstanceVulnerabilityAssessmentRead, + Update: resourceMsSqlManagedInstanceVulnerabilityAssessmentCreateUpdate, + Delete: resourceMsSqlManagedInstanceVulnerabilityAssessmentDelete, + + Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { + _, err := parse.ManagedInstanceVulnerabilityAssessmentID(id) + return err + }), + + Timeouts: &pluginsdk.ResourceTimeout{ + Create: pluginsdk.DefaultTimeout(60 * time.Minute), + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + Update: pluginsdk.DefaultTimeout(60 * time.Minute), + Delete: pluginsdk.DefaultTimeout(60 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "managed_instance_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.ManagedInstanceID, + }, + + "storage_container_path": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "storage_account_access_key": { + Type: pluginsdk.TypeString, + Optional: true, + Sensitive: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "storage_container_sas_key": { + Type: pluginsdk.TypeString, + Optional: true, + Sensitive: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "recurring_scans": { + Type: pluginsdk.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "email_subscription_admins": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + }, + + "emails": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + + "enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + }, + }, + }, + }, + } +} + +func resourceMsSqlManagedInstanceVulnerabilityAssessmentCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).MSSQL.ManagedInstanceVulnerabilityAssessmentsClient + ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) + defer cancel() + + instanceId, err := parse.ManagedInstanceID(d.Get("managed_instance_id").(string)) + if err != nil { + return err + } + + id := parse.NewManagedInstanceVulnerabilityAssessmentID(instanceId.SubscriptionId, instanceId.ResourceGroup, instanceId.Name, "default") + + vulnerabilityAssessment := sql.ManagedInstanceVulnerabilityAssessment{ + ManagedInstanceVulnerabilityAssessmentProperties: &sql.ManagedInstanceVulnerabilityAssessmentProperties{ + StorageContainerPath: utils.String(d.Get("storage_container_path").(string)), + StorageAccountAccessKey: utils.String(d.Get("storage_account_access_key").(string)), + StorageContainerSasKey: utils.String(d.Get("storage_container_sas_key").(string)), + RecurringScans: expandRecurringScans(d), + }, + } + + _, err = client.CreateOrUpdate(ctx, id.ResourceGroup, id.ManagedInstanceName, vulnerabilityAssessment) + if err != nil { + return fmt.Errorf("updataing %s: %v", id.ID(), err) + } + + d.SetId(id.ID()) + + return resourceMsSqlManagedInstanceVulnerabilityAssessmentRead(d, meta) +} + +func resourceMsSqlManagedInstanceVulnerabilityAssessmentRead(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).MSSQL.ManagedInstanceVulnerabilityAssessmentsClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.ManagedInstanceVulnerabilityAssessmentID(d.Id()) + if err != nil { + return err + } + + result, err := client.Get(ctx, id.ResourceGroup, id.ManagedInstanceName) + if err != nil { + if utils.ResponseWasNotFound(result.Response) { + log.Printf("[WARN] %s not found", id.ID()) + d.SetId("") + return nil + } + + return fmt.Errorf("making read request: %+v", err) + } + + if props := result.ManagedInstanceVulnerabilityAssessmentProperties; props != nil { + d.Set("storage_container_path", props.StorageContainerPath) + + if props.RecurringScans != nil { + if err := d.Set("recurring_scans", flattenRecurringScans(props.RecurringScans)); err != nil { + return fmt.Errorf("setting `recurring_scans`: %+v", err) + } + } + } + + return nil +} + +func resourceMsSqlManagedInstanceVulnerabilityAssessmentDelete(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).MSSQL.ManagedInstanceVulnerabilityAssessmentsClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.ManagedInstanceVulnerabilityAssessmentID(d.Id()) + if err != nil { + return err + } + + _, err = client.Delete(ctx, id.ResourceGroup, id.ManagedInstanceName) + if err != nil { + return fmt.Errorf("deleting Managed Instance Vulnerability Assessment %s: %+v", id.ID(), err) + } + + return nil +} diff --git a/internal/services/mssql/mssql_managed_instance_vulnerability_assessment_resource_test.go b/internal/services/mssql/mssql_managed_instance_vulnerability_assessment_resource_test.go new file mode 100644 index 000000000000..9a3b41887a6f --- /dev/null +++ b/internal/services/mssql/mssql_managed_instance_vulnerability_assessment_resource_test.go @@ -0,0 +1,132 @@ +package mssql_test + +import ( + "context" + "fmt" + "testing" + + "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/services/mssql/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type MsSqlManagedInstanceVulnerabilityAssessmentResource struct{} + +func TestAccAzureRMMssqlManagedInstanceVulnerabilityAssessment_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_mssql_managed_instance_vulnerability_assessment", "test") + r := MsSqlManagedInstanceVulnerabilityAssessmentResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("storage_account_access_key"), + }) +} + +func TestAccAzureRMMssqlManagedInstanceVulnerabilityAssessment_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_mssql_managed_instance_vulnerability_assessment", "test") + r := MsSqlManagedInstanceVulnerabilityAssessmentResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("storage_account_access_key"), + { + Config: r.update(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("storage_account_access_key"), + }) +} + +func (MsSqlManagedInstanceVulnerabilityAssessmentResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := parse.ManagedInstanceVulnerabilityAssessmentID(state.ID) + if err != nil { + return nil, err + } + + resp, err := client.MSSQL.ManagedInstanceVulnerabilityAssessmentsClient.Get(ctx, id.ResourceGroup, id.ManagedInstanceName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return nil, fmt.Errorf("%s does not exist", id.ID()) + } + + return nil, fmt.Errorf("reading %s: %v", id.ID(), err) + } + + return utils.Bool(resp.ID != nil), nil +} + +func (r MsSqlManagedInstanceVulnerabilityAssessmentResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%[1]s + +resource "azurerm_storage_account" "test" { + name = "accsa%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = "%[3]s" + account_tier = "Standard" + account_replication_type = "GRS" +} + +resource "azurerm_storage_container" "test" { + name = "acctestsc%[2]d" + storage_account_name = azurerm_storage_account.test.name + container_access_type = "private" +} + +resource "azurerm_mssql_managed_instance_vulnerability_assessment" "test" { + managed_instance_id = azurerm_mssql_managed_instance.test.id + storage_container_path = "${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}/" + storage_account_access_key = azurerm_storage_account.test.primary_access_key +} +`, MsSqlManagedInstanceResource{}.basic(data), data.RandomInteger, data.RandomString) +} + +func (r MsSqlManagedInstanceVulnerabilityAssessmentResource) update(data acceptance.TestData) string { + return fmt.Sprintf(` +%[1]s + +resource "azurerm_storage_account" "test" { + name = "accsa%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = "%[3]s" + account_tier = "Standard" + account_replication_type = "GRS" +} + +resource "azurerm_storage_container" "test" { + name = "acctestsc%[2]d" + storage_account_name = azurerm_storage_account.test.name + container_access_type = "private" +} + +resource "azurerm_mssql_managed_instance_vulnerability_assessment" "test" { + managed_instance_id = azurerm_mssql_managed_instance.test.id + storage_container_path = "${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}/" + storage_account_access_key = azurerm_storage_account.test.primary_access_key + + recurring_scans { + enabled = true + email_subscription_admins = true + emails = [ + "email@example1.com", + "email@example2.com" + ] + } +} +`, MsSqlManagedInstanceResource{}.basic(data), data.RandomInteger, data.RandomString) +} diff --git a/internal/services/mssql/parse/managed_instance_vulnerability_assessment.go b/internal/services/mssql/parse/managed_instance_vulnerability_assessment.go new file mode 100644 index 000000000000..b97d2f9eb54c --- /dev/null +++ b/internal/services/mssql/parse/managed_instance_vulnerability_assessment.go @@ -0,0 +1,75 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +type ManagedInstanceVulnerabilityAssessmentId struct { + SubscriptionId string + ResourceGroup string + ManagedInstanceName string + VulnerabilityAssessmentName string +} + +func NewManagedInstanceVulnerabilityAssessmentID(subscriptionId, resourceGroup, managedInstanceName, vulnerabilityAssessmentName string) ManagedInstanceVulnerabilityAssessmentId { + return ManagedInstanceVulnerabilityAssessmentId{ + SubscriptionId: subscriptionId, + ResourceGroup: resourceGroup, + ManagedInstanceName: managedInstanceName, + VulnerabilityAssessmentName: vulnerabilityAssessmentName, + } +} + +func (id ManagedInstanceVulnerabilityAssessmentId) String() string { + segments := []string{ + fmt.Sprintf("Vulnerability Assessment Name %q", id.VulnerabilityAssessmentName), + fmt.Sprintf("Managed Instance Name %q", id.ManagedInstanceName), + fmt.Sprintf("Resource Group %q", id.ResourceGroup), + } + segmentsStr := strings.Join(segments, " / ") + return fmt.Sprintf("%s: (%s)", "Managed Instance Vulnerability Assessment", segmentsStr) +} + +func (id ManagedInstanceVulnerabilityAssessmentId) ID() string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Sql/managedInstances/%s/vulnerabilityAssessments/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.ManagedInstanceName, id.VulnerabilityAssessmentName) +} + +// ManagedInstanceVulnerabilityAssessmentID parses a ManagedInstanceVulnerabilityAssessment ID into an ManagedInstanceVulnerabilityAssessmentId struct +func ManagedInstanceVulnerabilityAssessmentID(input string) (*ManagedInstanceVulnerabilityAssessmentId, error) { + id, err := resourceids.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := ManagedInstanceVulnerabilityAssessmentId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + if resourceId.ManagedInstanceName, err = id.PopSegment("managedInstances"); err != nil { + return nil, err + } + if resourceId.VulnerabilityAssessmentName, err = id.PopSegment("vulnerabilityAssessments"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/internal/services/mssql/parse/managed_instance_vulnerability_assessment_test.go b/internal/services/mssql/parse/managed_instance_vulnerability_assessment_test.go new file mode 100644 index 000000000000..c9c33f81ac52 --- /dev/null +++ b/internal/services/mssql/parse/managed_instance_vulnerability_assessment_test.go @@ -0,0 +1,128 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "testing" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +var _ resourceids.Id = ManagedInstanceVulnerabilityAssessmentId{} + +func TestManagedInstanceVulnerabilityAssessmentIDFormatter(t *testing.T) { + actual := NewManagedInstanceVulnerabilityAssessmentID("12345678-1234-9876-4563-123456789012", "resGroup1", "instance1", "assessment1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Sql/managedInstances/instance1/vulnerabilityAssessments/assessment1" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestManagedInstanceVulnerabilityAssessmentID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *ManagedInstanceVulnerabilityAssessmentId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing ManagedInstanceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Sql/", + Error: true, + }, + + { + // missing value for ManagedInstanceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Sql/managedInstances/", + Error: true, + }, + + { + // missing VulnerabilityAssessmentName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Sql/managedInstances/instance1/", + Error: true, + }, + + { + // missing value for VulnerabilityAssessmentName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Sql/managedInstances/instance1/vulnerabilityAssessments/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Sql/managedInstances/instance1/vulnerabilityAssessments/assessment1", + Expected: &ManagedInstanceVulnerabilityAssessmentId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "resGroup1", + ManagedInstanceName: "instance1", + VulnerabilityAssessmentName: "assessment1", + }, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.SQL/MANAGEDINSTANCES/INSTANCE1/VULNERABILITYASSESSMENTS/ASSESSMENT1", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := ManagedInstanceVulnerabilityAssessmentID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.ManagedInstanceName != v.Expected.ManagedInstanceName { + t.Fatalf("Expected %q but got %q for ManagedInstanceName", v.Expected.ManagedInstanceName, actual.ManagedInstanceName) + } + if actual.VulnerabilityAssessmentName != v.Expected.VulnerabilityAssessmentName { + t.Fatalf("Expected %q but got %q for VulnerabilityAssessmentName", v.Expected.VulnerabilityAssessmentName, actual.VulnerabilityAssessmentName) + } + } +} diff --git a/internal/services/mssql/registration.go b/internal/services/mssql/registration.go index 813ba1adcbfa..754964a90a60 100644 --- a/internal/services/mssql/registration.go +++ b/internal/services/mssql/registration.go @@ -46,6 +46,7 @@ func (r Registration) SupportedResources() map[string]*pluginsdk.Resource { "azurerm_mssql_elasticpool": resourceMsSqlElasticPool(), "azurerm_mssql_job_agent": resourceMsSqlJobAgent(), "azurerm_mssql_job_credential": resourceMsSqlJobCredential(), + "azurerm_mssql_managed_instance_vulnerability_assessment": resourceMsSqlManagedInstanceVulnerabilityAssessment(), "azurerm_mssql_firewall_rule": resourceMsSqlFirewallRule(), "azurerm_mssql_outbound_firewall_rule": resourceMsSqlOutboundFirewallRule(), "azurerm_mssql_server": resourceMsSqlServer(), diff --git a/internal/services/mssql/resourceids.go b/internal/services/mssql/resourceids.go index b839673397c3..404478a733c0 100644 --- a/internal/services/mssql/resourceids.go +++ b/internal/services/mssql/resourceids.go @@ -13,6 +13,7 @@ package mssql //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ManagedDatabase -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Sql/managedInstances/instance1/databases/database1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ManagedInstance -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Sql/managedInstances/instance1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ManagedInstanceAzureActiveDirectoryAdministrator -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Sql/managedInstances/instance1/administrators/activeDirectory +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ManagedInstanceVulnerabilityAssessment -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Sql/managedInstances/instance1/vulnerabilityAssessments/assessment1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=OutboundFirewallRule -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Sql/servers/server1/outboundFirewallRules/fqdn1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=RecoverableDatabase -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Sql/servers/server1/recoverabledatabases/database1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Server -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Sql/servers/server1 diff --git a/internal/services/mssql/validate/managed_instance_vulnerability_assessment_id.go b/internal/services/mssql/validate/managed_instance_vulnerability_assessment_id.go new file mode 100644 index 000000000000..e5bc3d46b3b6 --- /dev/null +++ b/internal/services/mssql/validate/managed_instance_vulnerability_assessment_id.go @@ -0,0 +1,23 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/internal/services/mssql/parse" +) + +func ManagedInstanceVulnerabilityAssessmentID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := parse.ManagedInstanceVulnerabilityAssessmentID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/internal/services/mssql/validate/managed_instance_vulnerability_assessment_id_test.go b/internal/services/mssql/validate/managed_instance_vulnerability_assessment_id_test.go new file mode 100644 index 000000000000..b701e3522463 --- /dev/null +++ b/internal/services/mssql/validate/managed_instance_vulnerability_assessment_id_test.go @@ -0,0 +1,88 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import "testing" + +func TestManagedInstanceVulnerabilityAssessmentID(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + + { + // empty + Input: "", + Valid: false, + }, + + { + // missing SubscriptionId + Input: "/", + Valid: false, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Valid: false, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Valid: false, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Valid: false, + }, + + { + // missing ManagedInstanceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Sql/", + Valid: false, + }, + + { + // missing value for ManagedInstanceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Sql/managedInstances/", + Valid: false, + }, + + { + // missing VulnerabilityAssessmentName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Sql/managedInstances/instance1/", + Valid: false, + }, + + { + // missing value for VulnerabilityAssessmentName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Sql/managedInstances/instance1/vulnerabilityAssessments/", + Valid: false, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Sql/managedInstances/instance1/vulnerabilityAssessments/assessment1", + Valid: true, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.SQL/MANAGEDINSTANCES/INSTANCE1/VULNERABILITYASSESSMENTS/ASSESSMENT1", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := ManagedInstanceVulnerabilityAssessmentID(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} diff --git a/website/docs/r/mssql_managed_instance_vulnerability_assessment.html.markdown b/website/docs/r/mssql_managed_instance_vulnerability_assessment.html.markdown new file mode 100644 index 000000000000..c477387856a9 --- /dev/null +++ b/website/docs/r/mssql_managed_instance_vulnerability_assessment.html.markdown @@ -0,0 +1,114 @@ +--- +subcategory: "Database" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_mssql_managed_instance_vulnerability_assessment" +description: |- + Manages the Vulnerability Assessment for an MS Managed Instance. + +--- + +# azurerm_mssql_managed_instance_vulnerability_assessment + +Manages the Vulnerability Assessment for an MS Managed Instance. + +## Example Usage + +```hcl +resource "azurerm_resource_group" "example" { + name = "example-resources" + location = "West Europe" +} + +resource "azurerm_mssql_managed_instance" "example" { + name = "exampleInstance" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + + license_type = "BasePrice" + sku_name = "GP_Gen5" + storage_size_in_gb = 32 + subnet_id = azurerm_subnet.test.id + vcores = 4 + + administrator_login = "missadministrator" + administrator_login_password = "NCC-1701-D" +} + +resource "azurerm_storage_account" "example" { + name = "accteststorageaccount" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + account_tier = "Standard" + account_replication_type = "GRS" +} + +resource "azurerm_storage_container" "example" { + name = "accteststoragecontainer" + storage_account_name = azurerm_storage_account.example.name + container_access_type = "private" +} + +resource "azurerm_mssql_managed_instance_vulnerability_assessment" "example" { + managed_instance_id = azurerm_mssql_managed_instance.example.id + storage_container_path = "${azurerm_storage_account.example.primary_blob_endpoint}${azurerm_storage_container.example.name}/" + storage_account_access_key = azurerm_storage_account.example.primary_access_key + + recurring_scans { + enabled = true + email_subscription_admins = true + emails = [ + "email@example1.com", + "email@example2.com" + ] + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `manged_instance_id` - (Required) The id of the MS SQL Managed Instance. Changing this forces a new resource to be created. + +* `storage_container_path` - (Required) A blob storage container path to hold the scan results (e.g. https://myStorage.blob.core.windows.net/VaScans/). + +* `storage_account_access_key` - (Optional) Specifies the identifier key of the storage account for vulnerability assessment scan results. If `storage_container_sas_key` isn't specified, `storage_account_access_key` is required. + +-> **NOTE** The `storage_account_access_key` only applies if the storage account is not behind a virtual network or a firewall. + +* `storage_container_sas_key` - (Optional) A shared access signature (SAS Key) that has write access to the blob container specified in `storage_container_path` parameter. If `storage_account_access_key` isn't specified, `storage_container_sas_key` is required. + +-> **NOTE** The `storage_container_sas_key` only applies if the storage account is not behind a virtual network or a firewall. + +* `recurring_scans` - (Optional) The recurring scans settings. The `recurring_scans` block supports fields documented below. + +--- + +`recurring_scans` supports the following: + +* `enabled` - (Optional) Boolean flag which specifies if recurring scans is enabled or disabled. Defaults to `false`. +* `email_subscription_admins` - (Optional) Boolean flag which specifies if the schedule scan notification will be sent to the subscription administrators. Defaults to `true`. +* `emails` - (Optional) Specifies an array of e-mail addresses to which the scan notification is sent. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the Vulnerability Assessment. + +## 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 Vulnerability Assessment. +* `update` - (Defaults to 30 minutes) Used when updating the Vulnerability Assessment. +* `read` - (Defaults to 5 minutes) Used when retrieving the Vulnerability Assessment. +* `delete` - (Defaults to 30 minutes) Used when deleting the Vulnerability Assessment. + +## Import + +The Vulnerability Assessment can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_mssql_managed_instance_vulnerability_assessment.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/acceptanceTestResourceGroup1/providers/Microsoft.Sql/managedInstances/instance1/vulnerabilityAssessments/Default +```