Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New resource - azurerm_sentinel_data_connector_aws_s3 #16440

Merged
merged 3 commits into from
May 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions internal/services/sentinel/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,6 @@ func (r Registration) Resources() []sdk.Resource {
return []sdk.Resource{
WatchlistResource{},
WatchlistItemResource{},
DataConnectorAwsS3Resource{},
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
loganalyticsParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/loganalytics/parse"
loganalyticsValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/loganalytics/validate"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/sentinel/parse"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/sentinel/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"
Expand Down Expand Up @@ -54,7 +55,7 @@ func resourceSentinelDataConnectorAwsCloudTrail() *pluginsdk.Resource {
"aws_role_arn": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validation.StringIsNotEmpty,
ValidateFunc: validate.IsARN,
},
},
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
package sentinel

import (
"context"
"fmt"
"time"

"github.com/Azure/azure-sdk-for-go/services/preview/securityinsight/mgmt/2021-09-01-preview/securityinsight"
"github.com/hashicorp/terraform-provider-azurerm/internal/sdk"
loganalyticsParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/loganalytics/parse"
loganalyticsValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/loganalytics/validate"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/sentinel/parse"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/sentinel/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/utils"
)

type DataConnectorAwsS3Resource struct{}

var _ sdk.ResourceWithUpdate = DataConnectorAwsS3Resource{}
var _ sdk.ResourceWithCustomImporter = DataConnectorAwsS3Resource{}

type DataConnectorAwsS3Model struct {
Name string `tfschema:"name"`
LogAnalyticsWorkspaceId string `tfschema:"log_analytics_workspace_id"`
AwsRoleArm string `tfschema:"aws_role_arn"`
DestinationTable string `tfschema:"destination_table"`
SqsUrls []string `tfschema:"sqs_urls"`
}

func (r DataConnectorAwsS3Resource) Arguments() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{
"name": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringIsNotEmpty,
},

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

"aws_role_arn": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validate.IsARN,
},

"destination_table": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validation.StringIsNotEmpty,
},

"sqs_urls": {
Type: pluginsdk.TypeList,
Required: true,
Elem: &pluginsdk.Schema{
Type: pluginsdk.TypeString,
ValidateFunc: validation.StringIsNotEmpty,
},
},
}
}

func (r DataConnectorAwsS3Resource) Attributes() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{}
}

func (r DataConnectorAwsS3Resource) ResourceType() string {
return "azurerm_sentinel_data_connector_aws_s3"
}

func (r DataConnectorAwsS3Resource) ModelObject() interface{} {
return &DataConnectorAwsS3Model{}
}

func (r DataConnectorAwsS3Resource) IDValidationFunc() pluginsdk.SchemaValidateFunc {
return validate.DataConnectorID
}

func (r DataConnectorAwsS3Resource) CustomImporter() sdk.ResourceRunFunc {
return func(ctx context.Context, metadata sdk.ResourceMetaData) error {
_, err := importSentinelDataConnector(securityinsight.DataConnectorKindAmazonWebServicesS3)(ctx, metadata.ResourceData, metadata.Client)
return err
}
}

func (r DataConnectorAwsS3Resource) Create() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 30 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.Sentinel.DataConnectorsClient

var plan DataConnectorAwsS3Model
if err := metadata.Decode(&plan); err != nil {
return fmt.Errorf("decoding %+v", err)
}

workspaceId, err := loganalyticsParse.LogAnalyticsWorkspaceID(plan.LogAnalyticsWorkspaceId)
if err != nil {
return err
}

id := parse.NewDataConnectorID(workspaceId.SubscriptionId, workspaceId.ResourceGroup, workspaceId.WorkspaceName, plan.Name)
existing, err := client.Get(ctx, id.ResourceGroup, id.WorkspaceName, id.Name)
if err != nil {
if !utils.ResponseWasNotFound(existing.Response) {
return fmt.Errorf("checking for presence of existing %s: %+v", id, err)
}
}
if !utils.ResponseWasNotFound(existing.Response) {
return metadata.ResourceRequiresImport(r.ResourceType(), id)
}

params := securityinsight.AwsS3DataConnector{
Name: &plan.Name,
AwsS3DataConnectorProperties: &securityinsight.AwsS3DataConnectorProperties{
DestinationTable: utils.String(plan.DestinationTable),
SqsUrls: &plan.SqsUrls,
RoleArn: utils.String(plan.AwsRoleArm),
DataTypes: &securityinsight.AwsS3DataConnectorDataTypes{
Logs: &securityinsight.AwsS3DataConnectorDataTypesLogs{
State: securityinsight.DataTypeStateEnabled,
},
},
},
Kind: securityinsight.KindBasicDataConnectorKindAmazonWebServicesS3,
}
if _, err = client.CreateOrUpdate(ctx, id.ResourceGroup, id.WorkspaceName, id.Name, params); err != nil {
return fmt.Errorf("creating %s: %+v", id, err)
}

metadata.SetID(id)
return nil
},
}
}

func (r DataConnectorAwsS3Resource) Read() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 5 * time.Minute,

Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.Sentinel.DataConnectorsClient
id, err := parse.DataConnectorID(metadata.ResourceData.Id())
if err != nil {
return err
}
workspaceId := loganalyticsParse.NewLogAnalyticsWorkspaceID(id.SubscriptionId, id.ResourceGroup, id.WorkspaceName)

existing, err := client.Get(ctx, id.ResourceGroup, id.WorkspaceName, id.Name)
if err != nil {
if utils.ResponseWasNotFound(existing.Response) {
return metadata.MarkAsGone(id)
}
return fmt.Errorf("retrieving %s: %+v", id, err)
}

dc, ok := existing.Value.(securityinsight.AwsS3DataConnector)
if !ok {
return fmt.Errorf("%s was not an AWS S3 Data Connector", id)
}

model := DataConnectorAwsS3Model{
Name: id.Name,
LogAnalyticsWorkspaceId: workspaceId.ID(),
}

if prop := dc.AwsS3DataConnectorProperties; prop != nil {
if prop.RoleArn != nil {
model.AwsRoleArm = *prop.RoleArn
}
if prop.DestinationTable != nil {
model.DestinationTable = *prop.DestinationTable
}
if prop.SqsUrls != nil {
model.SqsUrls = *prop.SqsUrls
}
}

return metadata.Encode(&model)
},
}
}

func (DataConnectorAwsS3Resource) Update() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 30 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
id, err := parse.DataConnectorID(metadata.ResourceData.Id())
if err != nil {
return err
}

var plan DataConnectorAwsS3Model
if err := metadata.Decode(&plan); err != nil {
return err
}

client := metadata.Client.Sentinel.DataConnectorsClient

resp, err := client.Get(ctx, id.ResourceGroup, id.WorkspaceName, id.Name)
if err != nil {
return fmt.Errorf("retrieving %s: %+v", id, err)
}

params, ok := resp.Value.(securityinsight.AwsS3DataConnector)
if !ok {
return fmt.Errorf("%s was not an AWS S3 Data Connector", id)
}

if props := params.AwsS3DataConnectorProperties; props != nil {
if metadata.ResourceData.HasChange("aws_role_arn") {
props.RoleArn = &plan.AwsRoleArm
}
if metadata.ResourceData.HasChange("destination_table") {
props.DestinationTable = &plan.DestinationTable
}
if metadata.ResourceData.HasChange("sqs_urls") {
props.SqsUrls = &plan.SqsUrls
}
}

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

return nil
},
}
}

func (r DataConnectorAwsS3Resource) Delete() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 30 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.Sentinel.DataConnectorsClient

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

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

return nil
},
}
}
19 changes: 19 additions & 0 deletions internal/services/sentinel/validate/aws_arn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package validate

import (
"fmt"
"strings"
)

func IsARN(i interface{}, k string) (warnings []string, errors []error) {
v, ok := i.(string)
if !ok {
errors = append(errors, fmt.Errorf("expected type of %s to be string", k))
return
}
// Referencing https://github.com/aws/aws-sdk-go/blob/e8afe81156c70d5bf7b6d2ed5aeeb609ea3ba3f8/aws/arn/arn.go#L81
if !(strings.HasPrefix(v, "arn:") && strings.Count(v, ":") >= 5) {
errors = append(errors, fmt.Errorf("invalid ARN"))
}
return warnings, errors
}
94 changes: 94 additions & 0 deletions website/docs/r/sentinel_data_connector_aws_s3.html.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
---
subcategory: "Sentinel"
layout: "azurerm"
page_title: "Azure Resource Manager: azurerm_sentinel_data_connector_aws_s3"
description: |-
Manages a AWS S3 Data Connector.
---

# azurerm_sentinel_data_connector_aws_s3

Manages a AWS S3 Data Connector.

## Example Usage

```hcl
resource "azurerm_resource_group" "example" {
name = "example-rg"
location = "West Europe"
}

resource "azurerm_log_analytics_workspace" "example" {
name = "example-workspace"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
sku = "PerGB2018"
}

resource "azurerm_log_analytics_solution" "example" {
solution_name = "SecurityInsights"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
workspace_resource_id = azurerm_log_analytics_workspace.example.id
workspace_name = azurerm_log_analytics_workspace.example.name

plan {
publisher = "Microsoft"
product = "OMSGallery/SecurityInsights"
}
}

resource "azurerm_sentinel_data_connector_aws_s3" "example" {
name = "example"
log_analytics_workspace_id = "TODO"
aws_role_arn = "TODO"
destination_table = "TODO"
sqs_urls = ["example"]
}

resource "azurerm_sentinel_data_connector_aws_s3" "example" {
name = "example"
log_analytics_workspace_id = azurerm_log_analytics_solution.example.workspace_resource_id
aws_role_arn = "arn:aws:iam::000000000000:role/role1"
destination_table = "AWSGuardDuty"
sqs_urls = ["https://sqs.us-east-1.amazonaws.com/000000000000/example"]
depends_on = [azurerm_log_analytics_solution.example]
}
```

## Arguments Reference

The following arguments are supported:

* `name` - (Required) The name which should be used for this AWS S3 Data Connector. Changing this forces a new AWS S3 Data Connector to be created.

* `log_analytics_workspace_id` - (Required) The ID of the Log Analytics Workspace that this AWS S3 Data Connector resides in. Changing this forces a new AWS S3 Data Connector to be created.

* `aws_role_arn` - (Required) The ARN of the AWS role, which is connected to this AWS CloudTrail Data Connector. See the [Azure document](https://docs.microsoft.com/en-us/azure/sentinel/connect-aws?tabs=s3#create-an-aws-assumed-role-and-grant-access-to-the-aws-sentinel-account) for details.

* `destination_table` - (Required) The name of the Log Analytics table that will store the ingested data.

* `sqs_urls` - (Required) Specifies a list of AWS SQS urls for the AWS S3 Data Connector.

## Attributes Reference

In addition to the Arguments listed above - the following Attributes are exported:

* `id` - The ID of the AWS S3 Data Connector.

## 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 AWS S3 Data Connector.
* `read` - (Defaults to 5 minutes) Used when retrieving the AWS S3 Data Connector.
* `update` - (Defaults to 30 minutes) Used when updating the AWS S3 Data Connector.
* `delete` - (Defaults to 30 minutes) Used when deleting the AWS S3 Data Connector.

## Import

AWS S3 Data Connectors can be imported using the `resource id`, e.g.

```shell
terraform import azurerm_sentinel_data_connector_aws_s3.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.OperationalInsights/workspaces/workspace1/providers/Microsoft.SecurityInsights/dataConnectors/dc1
```