Skip to content

Commit

Permalink
New resource: azurerm_sentinel_data_connector_dynamics_365 (#18859)
Browse files Browse the repository at this point in the history
Co-authored-by: stephybun <[email protected]>
  • Loading branch information
magodo and stephybun authored Oct 19, 2022
1 parent db6690f commit f204d46
Show file tree
Hide file tree
Showing 5 changed files with 431 additions and 0 deletions.
1 change: 1 addition & 0 deletions internal/services/sentinel/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ func (r Registration) Resources() []sdk.Resource {
WatchlistResource{},
WatchlistItemResource{},
DataConnectorAwsS3Resource{},
DataConnectorDynamics365Resource{},
DataConnectorOffice365ProjectResource{},
DataConnectorOfficePowerBIResource{},
DataConnectorOfficeIRMResource{},
Expand Down
2 changes: 2 additions & 0 deletions internal/services/sentinel/sentinel_data_connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ func assertDataConnectorKind(dc securityinsight.BasicDataConnector, expectKind s
kind = securityinsight.DataConnectorKindMicrosoftCloudAppSecurity
case securityinsight.TIDataConnector:
kind = securityinsight.DataConnectorKindThreatIntelligence
case securityinsight.Dynamics365DataConnector:
kind = securityinsight.DataConnectorKindDynamics365
case securityinsight.Office365ProjectDataConnector:
kind = securityinsight.DataConnectorKindOffice365Project
case securityinsight.OfficeIRMDataConnector:
Expand Down
194 changes: 194 additions & 0 deletions internal/services/sentinel/sentinel_data_connector_dynamics_365.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
package sentinel

import (
"context"
"fmt"
"time"

"github.com/Azure/azure-sdk-for-go/services/preview/securityinsight/mgmt/2022-01-01-preview/securityinsight"
"github.com/hashicorp/go-azure-sdk/resource-manager/operationalinsights/2020-08-01/workspaces"
"github.com/hashicorp/terraform-provider-azurerm/internal/sdk"
"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 DataConnectorDynamics365Resource struct{}

var _ sdk.ResourceWithCustomImporter = DataConnectorDynamics365Resource{}

type DataConnectorDynamics365Model struct {
Name string `tfschema:"name"`
LogAnalyticsWorkspaceId string `tfschema:"log_analytics_workspace_id"`
TenantId string `tfschema:"tenant_id"`
}

func (r DataConnectorDynamics365Resource) 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: workspaces.ValidateWorkspaceID,
},

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

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

func (r DataConnectorDynamics365Resource) ResourceType() string {
return "azurerm_sentinel_data_connector_dynamics_365"
}

func (r DataConnectorDynamics365Resource) ModelObject() interface{} {
return &DataConnectorDynamics365Model{}
}

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

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

func (r DataConnectorDynamics365Resource) 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 DataConnectorDynamics365Model
if err := metadata.Decode(&plan); err != nil {
return fmt.Errorf("decoding %+v", err)
}

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

id := parse.NewDataConnectorID(workspaceId.SubscriptionId, workspaceId.ResourceGroupName, 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)
}

tenantId := plan.TenantId
if tenantId == "" {
tenantId = metadata.Client.Account.TenantId
}

params := securityinsight.Dynamics365DataConnector{
Name: &plan.Name,
Dynamics365DataConnectorProperties: &securityinsight.Dynamics365DataConnectorProperties{
TenantID: &tenantId,
DataTypes: &securityinsight.Dynamics365DataConnectorDataTypes{
Dynamics365CdsActivities: &securityinsight.Dynamics365DataConnectorDataTypesDynamics365CdsActivities{
State: securityinsight.DataTypeStateEnabled,
},
},
},
Kind: securityinsight.KindBasicDataConnectorKindDynamics365,
}

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 DataConnectorDynamics365Resource) 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 := workspaces.NewWorkspaceID(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.Dynamics365DataConnector)
if !ok {
return fmt.Errorf("%s was not an Dynamics 365 Data Connector", id)
}

var tenantId string
if props := dc.Dynamics365DataConnectorProperties; props != nil {
if props.TenantID != nil {
tenantId = *props.TenantID
}
}

model := DataConnectorDynamics365Model{
Name: id.Name,
LogAnalyticsWorkspaceId: workspaceId.ID(),
TenantId: tenantId,
}

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

func (r DataConnectorDynamics365Resource) 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
},
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package sentinel_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/sentinel/parse"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
"github.com/hashicorp/terraform-provider-azurerm/utils"
)

type SentinelDataConnectorDynamics365Resource struct{}

func TestAccSentinelDataConnectorDynamics365_basic(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_sentinel_data_connector_dynamics_365", "test")
r := SentinelDataConnectorDynamics365Resource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.basic(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
})
}

func TestAccSentinelDataConnectorDynamics365_complete(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_sentinel_data_connector_dynamics_365", "test")
r := SentinelDataConnectorDynamics365Resource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.complete(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
})
}

func TestAccSentinelDataConnectorDynamics365_requiresImport(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_sentinel_data_connector_dynamics_365", "test")
r := SentinelDataConnectorDynamics365Resource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.basic(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.RequiresImportErrorStep(r.requiresImport),
})
}

func (r SentinelDataConnectorDynamics365Resource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) {
client := clients.Sentinel.DataConnectorsClient

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

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

return utils.Bool(true), nil
}

func (r SentinelDataConnectorDynamics365Resource) basic(data acceptance.TestData) string {
template := r.template(data)
return fmt.Sprintf(`
%s
resource "azurerm_sentinel_data_connector_dynamics_365" "test" {
name = "accTestDC-%d"
log_analytics_workspace_id = azurerm_log_analytics_workspace.test.id
depends_on = [azurerm_log_analytics_solution.test]
}
`, template, data.RandomInteger)
}

func (r SentinelDataConnectorDynamics365Resource) complete(data acceptance.TestData) string {
template := r.template(data)
return fmt.Sprintf(`
%s
data "azurerm_client_config" "test" {}
resource "azurerm_sentinel_data_connector_dynamics_365" "test" {
name = "accTestDC-%d"
log_analytics_workspace_id = azurerm_log_analytics_workspace.test.id
tenant_id = data.azurerm_client_config.test.tenant_id
depends_on = [azurerm_log_analytics_solution.test]
}
`, template, data.RandomInteger)
}

func (r SentinelDataConnectorDynamics365Resource) requiresImport(data acceptance.TestData) string {
template := r.basic(data)
return fmt.Sprintf(`
%s
resource "azurerm_sentinel_data_connector_dynamics_365" "import" {
name = azurerm_sentinel_data_connector_dynamics_365.test.name
log_analytics_workspace_id = azurerm_sentinel_data_connector_dynamics_365.test.log_analytics_workspace_id
}
`, template)
}

func (r SentinelDataConnectorDynamics365Resource) template(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "test" {
name = "acctestRG-sentinel-%d"
location = "%s"
}
resource "azurerm_log_analytics_workspace" "test" {
name = "acctestLAW-%d"
location = azurerm_resource_group.test.location
resource_group_name = azurerm_resource_group.test.name
sku = "PerGB2018"
}
resource "azurerm_log_analytics_solution" "test" {
solution_name = "SecurityInsights"
location = azurerm_resource_group.test.location
resource_group_name = azurerm_resource_group.test.name
workspace_resource_id = azurerm_log_analytics_workspace.test.id
workspace_name = azurerm_log_analytics_workspace.test.name
plan {
publisher = "Microsoft"
product = "OMSGallery/SecurityInsights"
}
}
`, data.RandomInteger, data.Locations.Primary, data.RandomInteger)
}
Loading

0 comments on commit f204d46

Please sign in to comment.