Skip to content

Commit

Permalink
new resource: `azurerm_sentinel_data_connector_microsoft_cloud_app_se…
Browse files Browse the repository at this point in the history
…curity` (#10668)

* new resource: `azurerm_sentinel_data_connector_microsoft_cloud_app_security`

* resolve review comments

* use type assertion rather than artificial asert function

* fix new resource check bug

* merge lines and use type assertion

* add clarification about etag

* always set property && doc update

* Update website/docs/r/sentinel_data_connector_microsoft_cloud_app_security.html.markdown

Co-authored-by: Tom Bamford <[email protected]>
  • Loading branch information
magodo and manicminer authored Mar 11, 2021
1 parent facba8a commit 1174556
Show file tree
Hide file tree
Showing 4 changed files with 495 additions and 0 deletions.
1 change: 1 addition & 0 deletions azurerm/internal/services/sentinel/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func (r Registration) SupportedResources() map[string]*schema.Resource {
"azurerm_sentinel_data_connector_azure_active_directory": resourceSentinelDataConnectorAzureActiveDirectory(),
"azurerm_sentinel_data_connector_azure_advanced_threat_protection": resourceSentinelDataConnectorAzureAdvancedThreatProtection(),
"azurerm_sentinel_data_connector_azure_security_center": resourceSentinelDataConnectorAzureSecurityCenter(),
"azurerm_sentinel_data_connector_microsoft_cloud_app_security": resourceSentinelDataConnectorMicrosoftCloudAppSecurity(),
"azurerm_sentinel_data_connector_office_365": resourceSentinelDataConnectorOffice365(),
"azurerm_sentinel_data_connector_threat_intelligence": resourceSentinelDataConnectorThreatIntelligence(),
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
package sentinel

import (
"fmt"
"log"
"strings"
"time"

"github.com/Azure/azure-sdk-for-go/services/preview/securityinsight/mgmt/2019-01-01-preview/securityinsight"
"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/tf"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients"
loganalyticsParse "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/loganalytics/parse"
loganalyticsValidate "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/loganalytics/validate"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/sentinel/parse"
azSchema "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/schema"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)

func resourceSentinelDataConnectorMicrosoftCloudAppSecurity() *schema.Resource {
return &schema.Resource{
Create: resourceSentinelDataConnectorMicrosoftCloudAppSecurityCreateUpdate,
Read: resourceSentinelDataConnectorMicrosoftCloudAppSecurityRead,
Update: resourceSentinelDataConnectorMicrosoftCloudAppSecurityCreateUpdate,
Delete: resourceSentinelDataConnectorMicrosoftCloudAppSecurityDelete,

Importer: azSchema.ValidateResourceIDPriorToImportThen(func(id string) error {
_, err := parse.DataConnectorID(id)
return err
}, importSentinelDataConnector(securityinsight.DataConnectorKindMicrosoftCloudAppSecurity)),

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: validation.StringIsNotEmpty,
},

"log_analytics_workspace_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: loganalyticsValidate.LogAnalyticsWorkspaceID,
},

"tenant_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: validation.IsUUID,
},

"alerts_enabled": {
Type: schema.TypeBool,
Optional: true,
Default: true,
},

"discovery_logs_enabled": {
Type: schema.TypeBool,
Optional: true,
Default: true,
},
},
}
}

func resourceSentinelDataConnectorMicrosoftCloudAppSecurityCreateUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Sentinel.DataConnectorsClient
ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d)
defer cancel()

workspaceId, err := loganalyticsParse.LogAnalyticsWorkspaceID(d.Get("log_analytics_workspace_id").(string))
if err != nil {
return err
}
name := d.Get("name").(string)
id := parse.NewDataConnectorID(workspaceId.SubscriptionId, workspaceId.ResourceGroup, workspaceId.WorkspaceName, name)

if d.IsNewResource() {
resp, err := client.Get(ctx, id.ResourceGroup, OperationalInsightsResourceProvider, id.WorkspaceName, name)
if err != nil {
if !utils.ResponseWasNotFound(resp.Response) {
return fmt.Errorf("checking for existing %s: %+v", id, err)
}
}

if !utils.ResponseWasNotFound(resp.Response) {
return tf.ImportAsExistsError("azurerm_sentinel_data_connector_microsoft_cloud_app_security", id.ID())
}
}

tenantId := d.Get("tenant_id").(string)
if tenantId == "" {
tenantId = meta.(*clients.Client).Account.TenantId
}

alertsEnabled := d.Get("alerts_enabled").(bool)
discoveryLogsEnabled := d.Get("discovery_logs_enabled").(bool)

// Service will not create the DC in case non of the toggle is enabled.
if !alertsEnabled && !discoveryLogsEnabled {
return fmt.Errorf("either `alerts_enabled` or `discovery_logs_enabled` should be `true`")
}

alertState := securityinsight.Enabled
if !alertsEnabled {
alertState = securityinsight.Disabled
}

discoveryLogsState := securityinsight.Enabled
if !discoveryLogsEnabled {
discoveryLogsState = securityinsight.Disabled
}

param := securityinsight.MCASDataConnector{
Name: &name,
MCASDataConnectorProperties: &securityinsight.MCASDataConnectorProperties{
TenantID: &tenantId,
DataTypes: &securityinsight.MCASDataConnectorDataTypes{
Alerts: &securityinsight.AlertsDataTypeOfDataConnectorAlerts{
State: alertState,
},
DiscoveryLogs: &securityinsight.MCASDataConnectorDataTypesDiscoveryLogs{
State: discoveryLogsState,
},
},
},
Kind: securityinsight.KindMicrosoftCloudAppSecurity,
}

// Service avoid concurrent updates of this resource via checking the "etag" to guarantee it is the same value as last Read.
// TODO: following code can be removed once the issue below is fixed:
// https://github.com/Azure/azure-rest-api-specs/issues/13203
if !d.IsNewResource() {
resp, err := client.Get(ctx, id.ResourceGroup, OperationalInsightsResourceProvider, id.WorkspaceName, name)
if err != nil {
return fmt.Errorf("retrieving %s: %+v", id, err)
}

dc, ok := resp.Value.(securityinsight.MCASDataConnector)
if !ok {
return fmt.Errorf("%s was not a Microsoft Cloud App Security Data Connector", id)
}
param.Etag = dc.Etag
}

if _, err = client.CreateOrUpdate(ctx, id.ResourceGroup, OperationalInsightsResourceProvider, id.WorkspaceName, id.Name, param); err != nil {
return fmt.Errorf("creating %s: %+v", id, err)
}

d.SetId(id.ID())

return resourceSentinelDataConnectorMicrosoftCloudAppSecurityRead(d, meta)
}

func resourceSentinelDataConnectorMicrosoftCloudAppSecurityRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Sentinel.DataConnectorsClient
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
defer cancel()

id, err := parse.DataConnectorID(d.Id())
if err != nil {
return err
}
workspaceId := loganalyticsParse.NewLogAnalyticsWorkspaceID(id.SubscriptionId, id.ResourceGroup, id.WorkspaceName)

resp, err := client.Get(ctx, id.ResourceGroup, OperationalInsightsResourceProvider, id.WorkspaceName, id.Name)
if err != nil {
if utils.ResponseWasNotFound(resp.Response) {
log.Printf("[DEBUG] %s was not found - removing from state!", id)
d.SetId("")
return nil
}

return fmt.Errorf("retrieving %s: %+v", id, err)
}

dc, ok := resp.Value.(securityinsight.MCASDataConnector)
if !ok {
return fmt.Errorf("%s was not a Microsoft Cloud App Security Data Connector", id)
}

d.Set("name", id.Name)
d.Set("log_analytics_workspace_id", workspaceId.ID())
d.Set("tenant_id", dc.TenantID)

var (
alertsEnabled bool
discoveryLogsEnabled bool
)
if dt := dc.DataTypes; dt != nil {
if alert := dt.Alerts; alert != nil {
alertsEnabled = strings.EqualFold(string(alert.State), string(securityinsight.Enabled))
}

if discoveryLogs := dt.DiscoveryLogs; discoveryLogs != nil {
discoveryLogsEnabled = strings.EqualFold(string(discoveryLogs.State), string(securityinsight.Enabled))
}
}
d.Set("discovery_logs_enabled", discoveryLogsEnabled)
d.Set("alerts_enabled", alertsEnabled)

return nil
}

func resourceSentinelDataConnectorMicrosoftCloudAppSecurityDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Sentinel.DataConnectorsClient
ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d)
defer cancel()

id, err := parse.DataConnectorID(d.Id())
if err != nil {
return err
}

if _, err = client.Delete(ctx, id.ResourceGroup, OperationalInsightsResourceProvider, id.WorkspaceName, id.Name); err != nil {
return fmt.Errorf("deleting %s: %+v", id, err)
}

return nil
}
Loading

0 comments on commit 1174556

Please sign in to comment.