diff --git a/internal/services/sentinel/azuresdkhacks/dataconnectors.go b/internal/services/sentinel/azuresdkhacks/dataconnectors.go new file mode 100644 index 000000000000..db3b9207ae7f --- /dev/null +++ b/internal/services/sentinel/azuresdkhacks/dataconnectors.go @@ -0,0 +1,162 @@ +package azuresdkhacks + +import ( + "context" + "net/http" + + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" + "github.com/Azure/go-autorest/autorest/validation" + securityinsight "github.com/tombuildsstuff/kermit/sdk/securityinsights/2022-10-01-preview/securityinsights" +) + +type DataConnectorsClient struct { + securityinsight.BaseClient +} + +func (client DataConnectorsClient) Get(ctx context.Context, resourceGroupName string, workspaceName string, dataConnectorID string) (result DataConnectorModel, err error) { + if err := validation.Validate([]validation.Validation{ + {TargetValue: client.SubscriptionID, + Constraints: []validation.Constraint{{Target: "client.SubscriptionID", Name: validation.MinLength, Rule: 1, Chain: nil}}}, + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}}}, + {TargetValue: workspaceName, + Constraints: []validation.Constraint{{Target: "workspaceName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "workspaceName", Name: validation.MinLength, Rule: 1, Chain: nil}}}}); err != nil { + return result, validation.NewError("securityinsight.DataConnectorsClient", "Get", err.Error()) + } + + req, err := client.GetPreparer(ctx, resourceGroupName, workspaceName, dataConnectorID) + if err != nil { + err = autorest.NewErrorWithError(err, "securityinsight.DataConnectorsClient", "Get", nil, "Failure preparing request") + return + } + + resp, err := client.GetSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "securityinsight.DataConnectorsClient", "Get", resp, "Failure sending request") + return + } + + result, err = client.GetResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "securityinsight.DataConnectorsClient", "Get", resp, "Failure responding to request") + return + } + + return +} + +func (client DataConnectorsClient) GetPreparer(ctx context.Context, resourceGroupName string, workspaceName string, dataConnectorID string) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "dataConnectorId": autorest.Encode("path", dataConnectorID), + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + "workspaceName": autorest.Encode("path", workspaceName), + } + + const APIVersion = "2022-10-01-preview" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.OperationalInsights/workspaces/{workspaceName}/providers/Microsoft.SecurityInsights/dataConnectors/{dataConnectorId}", pathParameters), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare((&http.Request{}).WithContext(ctx)) +} + +func (client DataConnectorsClient) GetSender(req *http.Request) (*http.Response, error) { + return client.Send(req, azure.DoRetryWithRegistration(client.Client)) +} + +func (client DataConnectorsClient) GetResponder(resp *http.Response) (result DataConnectorModel, err error) { + err = autorest.Respond( + resp, + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} + +func (client DataConnectorsClient) CreateOrUpdate(ctx context.Context, resourceGroupName string, workspaceName string, dataConnectorID string, dataConnector securityinsight.BasicDataConnector) (result DataConnectorModel, err error) { + if err := validation.Validate([]validation.Validation{ + {TargetValue: client.SubscriptionID, + Constraints: []validation.Constraint{{Target: "client.SubscriptionID", Name: validation.MinLength, Rule: 1, Chain: nil}}}, + {TargetValue: resourceGroupName, + Constraints: []validation.Constraint{{Target: "resourceGroupName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "resourceGroupName", Name: validation.MinLength, Rule: 1, Chain: nil}}}, + {TargetValue: workspaceName, + Constraints: []validation.Constraint{{Target: "workspaceName", Name: validation.MaxLength, Rule: 90, Chain: nil}, + {Target: "workspaceName", Name: validation.MinLength, Rule: 1, Chain: nil}}}}); err != nil { + return result, validation.NewError("securityinsight.DataConnectorsClient", "CreateOrUpdate", err.Error()) + } + + req, err := client.CreateOrUpdatePreparer(ctx, resourceGroupName, workspaceName, dataConnectorID, dataConnector) + if err != nil { + err = autorest.NewErrorWithError(err, "securityinsight.DataConnectorsClient", "CreateOrUpdate", nil, "Failure preparing request") + return + } + + resp, err := client.CreateOrUpdateSender(req) + if err != nil { + result.Response = autorest.Response{Response: resp} + err = autorest.NewErrorWithError(err, "securityinsight.DataConnectorsClient", "CreateOrUpdate", resp, "Failure sending request") + return + } + + result, err = client.CreateOrUpdateResponder(resp) + if err != nil { + err = autorest.NewErrorWithError(err, "securityinsight.DataConnectorsClient", "CreateOrUpdate", resp, "Failure responding to request") + return + } + + return +} + +// CreateOrUpdatePreparer prepares the CreateOrUpdate request. +func (client DataConnectorsClient) CreateOrUpdatePreparer(ctx context.Context, resourceGroupName string, workspaceName string, dataConnectorID string, dataConnector securityinsight.BasicDataConnector) (*http.Request, error) { + pathParameters := map[string]interface{}{ + "dataConnectorId": autorest.Encode("path", dataConnectorID), + "resourceGroupName": autorest.Encode("path", resourceGroupName), + "subscriptionId": autorest.Encode("path", client.SubscriptionID), + "workspaceName": autorest.Encode("path", workspaceName), + } + + const APIVersion = "2022-10-01-preview" + queryParameters := map[string]interface{}{ + "api-version": APIVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsContentType("application/json; charset=utf-8"), + autorest.AsPut(), + autorest.WithBaseURL(client.BaseURI), + autorest.WithPathParameters("/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.OperationalInsights/workspaces/{workspaceName}/providers/Microsoft.SecurityInsights/dataConnectors/{dataConnectorId}", pathParameters), + autorest.WithJSON(dataConnector), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare((&http.Request{}).WithContext(ctx)) +} + +// CreateOrUpdateSender sends the CreateOrUpdate request. The method will close the +// http.Response Body if it receives an error. +func (client DataConnectorsClient) CreateOrUpdateSender(req *http.Request) (*http.Response, error) { + return client.Send(req, azure.DoRetryWithRegistration(client.Client)) +} + +// CreateOrUpdateResponder handles the response to the CreateOrUpdate request. The method always +// closes the http.Response Body. +func (client DataConnectorsClient) CreateOrUpdateResponder(resp *http.Response) (result DataConnectorModel, err error) { + err = autorest.Respond( + resp, + azure.WithErrorUnlessStatusCode(http.StatusOK, http.StatusCreated), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + result.Response = autorest.Response{Response: resp} + return +} diff --git a/internal/services/sentinel/azuresdkhacks/models.go b/internal/services/sentinel/azuresdkhacks/models.go new file mode 100644 index 000000000000..aa9b2ed59fa0 --- /dev/null +++ b/internal/services/sentinel/azuresdkhacks/models.go @@ -0,0 +1,364 @@ +package azuresdkhacks + +import ( + "encoding/json" + "fmt" + "time" + + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/date" + securityinsight "github.com/tombuildsstuff/kermit/sdk/securityinsights/2022-10-01-preview/securityinsights" +) + +// TODO 4.0 check if this can be removed +// Hacking the SDK model, together with the Create and Get method for working around issue: https://github.com/Azure/azure-rest-api-specs/issues/21487 + +type DataConnectorModel struct { + autorest.Response `json:"-"` + Value securityinsight.BasicDataConnector `json:"value,omitempty"` +} + +func (dcm *DataConnectorModel) UnmarshalJSON(body []byte) error { + dc, err := unmarshalBasicDataConnector(body) + if err != nil { + return err + } + dcm.Value = dc + + return nil +} + +func unmarshalBasicDataConnector(body []byte) (securityinsight.BasicDataConnector, error) { + var m map[string]interface{} + err := json.Unmarshal(body, &m) + if err != nil { + return nil, err + } + + switch m["kind"] { + case string(securityinsight.KindBasicDataConnectorKindAzureActiveDirectory): + var adc securityinsight.AADDataConnector + err := json.Unmarshal(body, &adc) + return adc, err + case string(securityinsight.KindBasicDataConnectorKindMicrosoftThreatIntelligence): + var mdc securityinsight.MSTIDataConnector + err := json.Unmarshal(body, &mdc) + return mdc, err + case string(securityinsight.KindBasicDataConnectorKindMicrosoftThreatProtection): + var mdc securityinsight.MTPDataConnector + err := json.Unmarshal(body, &mdc) + return mdc, err + case string(securityinsight.KindBasicDataConnectorKindAzureAdvancedThreatProtection): + var adc securityinsight.AATPDataConnector + err := json.Unmarshal(body, &adc) + return adc, err + case string(securityinsight.KindBasicDataConnectorKindAzureSecurityCenter): + var adc securityinsight.ASCDataConnector + err := json.Unmarshal(body, &adc) + return adc, err + case string(securityinsight.KindBasicDataConnectorKindAmazonWebServicesCloudTrail): + var actdc securityinsight.AwsCloudTrailDataConnector + err := json.Unmarshal(body, &actdc) + return actdc, err + case string(securityinsight.KindBasicDataConnectorKindAmazonWebServicesS3): + var asdc securityinsight.AwsS3DataConnector + err := json.Unmarshal(body, &asdc) + return asdc, err + case string(securityinsight.KindBasicDataConnectorKindMicrosoftCloudAppSecurity): + var mdc securityinsight.MCASDataConnector + err := json.Unmarshal(body, &mdc) + return mdc, err + case string(securityinsight.KindBasicDataConnectorKindDynamics365): + var d3dc securityinsight.Dynamics365DataConnector + err := json.Unmarshal(body, &d3dc) + return d3dc, err + case string(securityinsight.KindBasicDataConnectorKindOfficeATP): + var oadc securityinsight.OfficeATPDataConnector + err := json.Unmarshal(body, &oadc) + return oadc, err + case string(securityinsight.KindBasicDataConnectorKindOffice365Project): + var o3pdc securityinsight.Office365ProjectDataConnector + err := json.Unmarshal(body, &o3pdc) + return o3pdc, err + case string(securityinsight.KindBasicDataConnectorKindOfficePowerBI): + var opbdc securityinsight.OfficePowerBIDataConnector + err := json.Unmarshal(body, &opbdc) + return opbdc, err + case string(securityinsight.KindBasicDataConnectorKindOfficeIRM): + var oidc securityinsight.OfficeIRMDataConnector + err := json.Unmarshal(body, &oidc) + return oidc, err + case string(securityinsight.KindBasicDataConnectorKindMicrosoftDefenderAdvancedThreatProtection): + var mdc securityinsight.MDATPDataConnector + err := json.Unmarshal(body, &mdc) + return mdc, err + case string(securityinsight.KindBasicDataConnectorKindOffice365): + var odc securityinsight.OfficeDataConnector + err := json.Unmarshal(body, &odc) + return odc, err + case string(securityinsight.KindBasicDataConnectorKindThreatIntelligence): + var tdc securityinsight.TIDataConnector + err := json.Unmarshal(body, &tdc) + return tdc, err + case string(securityinsight.KindBasicDataConnectorKindThreatIntelligenceTaxii): + var ttdc TiTaxiiDataConnector // using the hacked one + err := json.Unmarshal(body, &ttdc) + return ttdc, err + case string(securityinsight.KindBasicDataConnectorKindIOT): + var itdc securityinsight.IoTDataConnector + err := json.Unmarshal(body, &itdc) + return itdc, err + case string(securityinsight.KindBasicDataConnectorKindGenericUI): + var cudc securityinsight.CodelessUIDataConnector + err := json.Unmarshal(body, &cudc) + return cudc, err + case string(securityinsight.KindBasicDataConnectorKindAPIPolling): + var capdc securityinsight.CodelessAPIPollingDataConnector + err := json.Unmarshal(body, &capdc) + return capdc, err + default: + var dc securityinsight.DataConnector + err := json.Unmarshal(body, &dc) + return dc, err + } +} + +var _ securityinsight.BasicDataConnector = TiTaxiiDataConnector{} + +type TiTaxiiDataConnector struct { + *TiTaxiiDataConnectorProperties `json:"properties,omitempty"` + Kind securityinsight.KindBasicDataConnector `json:"kind,omitempty"` + Etag *string `json:"etag,omitempty"` + ID *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Type *string `json:"type,omitempty"` + SystemData *securityinsight.SystemData `json:"systemData,omitempty"` +} + +func (t TiTaxiiDataConnector) AsAADDataConnector() (*securityinsight.AADDataConnector, bool) { + return nil, false +} + +func (t TiTaxiiDataConnector) AsMSTIDataConnector() (*securityinsight.MSTIDataConnector, bool) { + return nil, false +} + +func (t TiTaxiiDataConnector) AsMTPDataConnector() (*securityinsight.MTPDataConnector, bool) { + return nil, false +} + +func (t TiTaxiiDataConnector) AsAATPDataConnector() (*securityinsight.AATPDataConnector, bool) { + return nil, false +} + +func (t TiTaxiiDataConnector) AsASCDataConnector() (*securityinsight.ASCDataConnector, bool) { + return nil, false +} + +func (t TiTaxiiDataConnector) AsAwsCloudTrailDataConnector() (*securityinsight.AwsCloudTrailDataConnector, bool) { + return nil, false +} + +func (t TiTaxiiDataConnector) AsAwsS3DataConnector() (*securityinsight.AwsS3DataConnector, bool) { + return nil, false +} + +func (t TiTaxiiDataConnector) AsMCASDataConnector() (*securityinsight.MCASDataConnector, bool) { + return nil, false +} + +func (t TiTaxiiDataConnector) AsDynamics365DataConnector() (*securityinsight.Dynamics365DataConnector, bool) { + return nil, false +} + +func (t TiTaxiiDataConnector) AsOfficeATPDataConnector() (*securityinsight.OfficeATPDataConnector, bool) { + return nil, false +} + +func (t TiTaxiiDataConnector) AsOffice365ProjectDataConnector() (*securityinsight.Office365ProjectDataConnector, bool) { + return nil, false +} + +func (t TiTaxiiDataConnector) AsOfficePowerBIDataConnector() (*securityinsight.OfficePowerBIDataConnector, bool) { + return nil, false +} + +func (t TiTaxiiDataConnector) AsOfficeIRMDataConnector() (*securityinsight.OfficeIRMDataConnector, bool) { + return nil, false +} + +func (t TiTaxiiDataConnector) AsMDATPDataConnector() (*securityinsight.MDATPDataConnector, bool) { + return nil, false +} + +func (t TiTaxiiDataConnector) AsOfficeDataConnector() (*securityinsight.OfficeDataConnector, bool) { + return nil, false +} + +func (t TiTaxiiDataConnector) AsTIDataConnector() (*securityinsight.TIDataConnector, bool) { + return nil, false +} + +func (t TiTaxiiDataConnector) AsTiTaxiiDataConnector() (*securityinsight.TiTaxiiDataConnector, bool) { + // This method is not used at all, only for implementing the interface. + return nil, false +} + +func (t TiTaxiiDataConnector) AsIoTDataConnector() (*securityinsight.IoTDataConnector, bool) { + return nil, false +} + +func (t TiTaxiiDataConnector) AsCodelessUIDataConnector() (*securityinsight.CodelessUIDataConnector, bool) { + return nil, false +} + +func (t TiTaxiiDataConnector) AsCodelessAPIPollingDataConnector() (*securityinsight.CodelessAPIPollingDataConnector, bool) { + return nil, false +} + +func (t TiTaxiiDataConnector) AsDataConnector() (*securityinsight.DataConnector, bool) { + return nil, false +} + +func (ttdc TiTaxiiDataConnector) MarshalJSON() ([]byte, error) { + ttdc.Kind = securityinsight.KindBasicDataConnectorKindThreatIntelligenceTaxii + objectMap := make(map[string]interface{}) + if ttdc.TiTaxiiDataConnectorProperties != nil { + objectMap["properties"] = ttdc.TiTaxiiDataConnectorProperties + } + if ttdc.Kind != "" { + objectMap["kind"] = ttdc.Kind + } + if ttdc.Etag != nil { + objectMap["etag"] = ttdc.Etag + } + return json.Marshal(objectMap) +} + +func (ttdc *TiTaxiiDataConnector) UnmarshalJSON(body []byte) error { + var m map[string]*json.RawMessage + err := json.Unmarshal(body, &m) + if err != nil { + return err + } + for k, v := range m { + switch k { + case "properties": + if v != nil { + var tiTaxiiDataConnectorProperties TiTaxiiDataConnectorProperties + err = json.Unmarshal(*v, &tiTaxiiDataConnectorProperties) + if err != nil { + return err + } + ttdc.TiTaxiiDataConnectorProperties = &tiTaxiiDataConnectorProperties + } + case "kind": + if v != nil { + var kind securityinsight.KindBasicDataConnector + err = json.Unmarshal(*v, &kind) + if err != nil { + return err + } + ttdc.Kind = kind + } + case "etag": + if v != nil { + var etag string + err = json.Unmarshal(*v, &etag) + if err != nil { + return err + } + ttdc.Etag = &etag + } + case "id": + if v != nil { + var ID string + err = json.Unmarshal(*v, &ID) + if err != nil { + return err + } + ttdc.ID = &ID + } + case "name": + if v != nil { + var name string + err = json.Unmarshal(*v, &name) + if err != nil { + return err + } + ttdc.Name = &name + } + case "type": + if v != nil { + var typeVar string + err = json.Unmarshal(*v, &typeVar) + if err != nil { + return err + } + ttdc.Type = &typeVar + } + case "systemData": + if v != nil { + var systemData securityinsight.SystemData + err = json.Unmarshal(*v, &systemData) + if err != nil { + return err + } + ttdc.SystemData = &systemData + } + } + } + + return nil +} + +type PollingFrequency string + +func (freq *PollingFrequency) UnmarshalJSON(body []byte) error { + switch string(body) { + case "0", string(PollingFrequencyOnceAMinute): + *freq = PollingFrequencyOnceAMinute + case "1", string(PollingFrequencyOnceAnHour): + *freq = PollingFrequencyOnceAnHour + case "2", string(PollingFrequencyOnceADay): + *freq = PollingFrequencyOnceADay + default: + return fmt.Errorf("unknown enum for pollingFrequency %s", string(body)) + } + return nil +} + +const ( + PollingFrequencyOnceAMinute PollingFrequency = "OnceAMinute" // API returns 0 + PollingFrequencyOnceAnHour PollingFrequency = "OnceAnHour" // API returns 1 + PollingFrequencyOnceADay PollingFrequency = "OnceADay" // API returns 2 +) + +type Time date.Time + +func (t *Time) UnmarshalJSON(data []byte) (err error) { + // Firstly, try to parse the date time via RFC3339, which is the expected format defined by Swagger. + // However, since the service issue (#21487), it currently doesn't return in this format. + // In order not to break the code once the service fix it, we keep this try at first. + if time, err := time.Parse(time.RFC3339, string(data)); err == nil { + t.Time = time + return nil + } + + // This is the format that the service returns at this moment, which is not the expected format (RFC3339). + t.Time, err = time.Parse(`"1/2/2006 15:04:05 PM -07:00"`, string(data)) + return err +} + +type TiTaxiiDataConnectorProperties struct { + WorkspaceID *string `json:"workspaceId,omitempty"` + FriendlyName *string `json:"friendlyName,omitempty"` + TaxiiServer *string `json:"taxiiServer,omitempty"` + CollectionID *string `json:"collectionId,omitempty"` + UserName *string `json:"userName,omitempty"` + Password *string `json:"password,omitempty"` + TaxiiLookbackPeriod *Time `json:"taxiiLookbackPeriod,omitempty"` + PollingFrequency PollingFrequency `json:"pollingFrequency,omitempty"` + DataTypes *securityinsight.TiTaxiiDataConnectorDataTypes `json:"dataTypes,omitempty"` + TenantID *string `json:"tenantId,omitempty"` +} diff --git a/internal/services/sentinel/registration.go b/internal/services/sentinel/registration.go index 836f97efd10f..7a4bde1515a1 100644 --- a/internal/services/sentinel/registration.go +++ b/internal/services/sentinel/registration.go @@ -69,5 +69,6 @@ func (r Registration) Resources() []sdk.Resource { DataConnectorOffice365ProjectResource{}, DataConnectorOfficePowerBIResource{}, DataConnectorOfficeIRMResource{}, + DataConnectorThreatIntelligenceTAXIIResource{}, } } diff --git a/internal/services/sentinel/sentinel_data_connector.go b/internal/services/sentinel/sentinel_data_connector.go index 51e5888ff725..0726682d0067 100644 --- a/internal/services/sentinel/sentinel_data_connector.go +++ b/internal/services/sentinel/sentinel_data_connector.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/sentinel/azuresdkhacks" "github.com/hashicorp/terraform-provider-azurerm/internal/services/sentinel/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" securityinsight "github.com/tombuildsstuff/kermit/sdk/securityinsights/2022-10-01-preview/securityinsights" @@ -17,7 +18,7 @@ func importSentinelDataConnector(expectKind securityinsight.DataConnectorKind) p return nil, err } - client := meta.(*clients.Client).Sentinel.DataConnectorsClient + client := azuresdkhacks.DataConnectorsClient{BaseClient: meta.(*clients.Client).Sentinel.DataConnectorsClient.BaseClient} resp, err := client.Get(ctx, id.ResourceGroup, id.WorkspaceName, id.Name) if err != nil { return nil, fmt.Errorf("retrieving Sentinel Alert Rule %q: %+v", id, err) @@ -65,6 +66,8 @@ func assertDataConnectorKind(dc securityinsight.BasicDataConnector, expectKind s kind = securityinsight.DataConnectorKindMicrosoftDefenderAdvancedThreatProtection case securityinsight.AwsS3DataConnector: kind = securityinsight.DataConnectorKindAmazonWebServicesS3 + case azuresdkhacks.TiTaxiiDataConnector: + kind = securityinsight.DataConnectorKindThreatIntelligenceTaxii } if expectKind != kind { return fmt.Errorf("Sentinel Data Connector has mismatched kind, expected: %q, got %q", expectKind, kind) diff --git a/internal/services/sentinel/sentinel_data_connector_threat_intelligence_taxii.go b/internal/services/sentinel/sentinel_data_connector_threat_intelligence_taxii.go new file mode 100644 index 000000000000..8f237b22e2e3 --- /dev/null +++ b/internal/services/sentinel/sentinel_data_connector_threat_intelligence_taxii.go @@ -0,0 +1,374 @@ +package sentinel + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/go-azure-sdk/resource-manager/operationalinsights/2022-10-01/workspaces" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/sentinel/azuresdkhacks" + "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" + securityinsight "github.com/tombuildsstuff/kermit/sdk/securityinsights/2022-10-01-preview/securityinsights" +) + +type DataConnectorThreatIntelligenceTAXIIResource struct{} + +var _ sdk.ResourceWithUpdate = DataConnectorThreatIntelligenceTAXIIResource{} + +type DataConnectorThreatIntelligenceTAXIIModel struct { + Name string `tfschema:"name"` + LogAnalyticsWorkspaceId string `tfschema:"log_analytics_workspace_id"` + DisplayName string `tfschema:"display_name"` + APIRootURL string `tfschema:"api_root_url"` + CollectionID string `tfschema:"collection_id"` + UserName string `tfschema:"user_name"` + Password string `tfschema:"password"` + PollingFrequency string `tfschema:"polling_frequency"` + LookbackDate string `tfschema:"lookback_date"` + TenantId string `tfschema:"tenant_id"` +} + +func (r DataConnectorThreatIntelligenceTAXIIResource) 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, + }, + "display_name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "api_root_url": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "collection_id": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "user_name": { + Type: pluginsdk.TypeString, + Optional: true, + Sensitive: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "password": { + Type: pluginsdk.TypeString, + Optional: true, + Sensitive: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "polling_frequency": { + Type: pluginsdk.TypeString, + Optional: true, + Default: string(securityinsight.PollingFrequencyOnceAnHour), + ValidateFunc: validation.StringInSlice([]string{ + string(securityinsight.PollingFrequencyOnceAMinute), + string(securityinsight.PollingFrequencyOnceAnHour), + string(securityinsight.PollingFrequencyOnceADay), + }, + false), + }, + "lookback_date": { + Type: pluginsdk.TypeString, + Optional: true, + Default: "1970-01-01T00:00:00Z", + ValidateFunc: validation.IsRFC3339Time, + }, + "tenant_id": { + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validation.IsUUID, + }, + } +} + +func (r DataConnectorThreatIntelligenceTAXIIResource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{} +} + +func (r DataConnectorThreatIntelligenceTAXIIResource) ResourceType() string { + return "azurerm_sentinel_data_connector_threat_intelligence_taxii" +} + +func (r DataConnectorThreatIntelligenceTAXIIResource) ModelObject() interface{} { + return &DataConnectorThreatIntelligenceTAXIIModel{} +} + +func (r DataConnectorThreatIntelligenceTAXIIResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return validate.DataConnectorID +} + +func (r DataConnectorThreatIntelligenceTAXIIResource) CustomImporter() sdk.ResourceRunFunc { + return func(ctx context.Context, metadata sdk.ResourceMetaData) error { + _, err := importSentinelDataConnector(securityinsight.DataConnectorKindThreatIntelligenceTaxii)(ctx, metadata.ResourceData, metadata.Client) + return err + } +} + +func (r DataConnectorThreatIntelligenceTAXIIResource) Create() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := azuresdkhacks.DataConnectorsClient{BaseClient: metadata.Client.Sentinel.DataConnectorsClient.BaseClient} + wspClient := metadata.Client.LogAnalytics.WorkspaceClient + + var plan DataConnectorThreatIntelligenceTAXIIModel + if err := metadata.Decode(&plan); err != nil { + return fmt.Errorf("decoding %+v", err) + } + + workspaceId, err := workspaces.ParseWorkspaceID(plan.LogAnalyticsWorkspaceId) + if err != nil { + return err + } + + wsp, err := wspClient.Get(ctx, *workspaceId) + if err != nil { + return fmt.Errorf("retrieving the workspace %s: %+v", workspaceId, err) + } + if wsp.Model == nil { + return fmt.Errorf("nil model of the workspace %s", workspaceId) + } + if wsp.Model.Properties == nil { + return fmt.Errorf("nil properties of the workspace %s", workspaceId) + } + if wsp.Model.Properties.CustomerId == nil { + return fmt.Errorf("nil workspace id of the workspace %s", workspaceId) + } + wspId := *wsp.Model.Properties.CustomerId + + 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 + } + + // Format is guaranteed by schema validation + lookbackDate, _ := time.Parse(time.RFC3339, plan.LookbackDate) + + params := azuresdkhacks.TiTaxiiDataConnector{ + Name: &plan.Name, + TiTaxiiDataConnectorProperties: &azuresdkhacks.TiTaxiiDataConnectorProperties{ + WorkspaceID: &wspId, + FriendlyName: &plan.DisplayName, + TaxiiServer: &plan.APIRootURL, + CollectionID: &plan.CollectionID, + PollingFrequency: azuresdkhacks.PollingFrequency(plan.PollingFrequency), + TaxiiLookbackPeriod: &azuresdkhacks.Time{ + Time: lookbackDate, + }, + DataTypes: &securityinsight.TiTaxiiDataConnectorDataTypes{ + TaxiiClient: &securityinsight.TiTaxiiDataConnectorDataTypesTaxiiClient{ + State: securityinsight.DataTypeStateEnabled, + }, + }, + TenantID: &tenantId, + }, + Kind: securityinsight.KindBasicDataConnectorKindThreatIntelligenceTaxii, + } + + if plan.UserName != "" { + params.TiTaxiiDataConnectorProperties.UserName = &plan.UserName + } + + if plan.Password != "" { + params.TiTaxiiDataConnectorProperties.Password = &plan.Password + } + + 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 DataConnectorThreatIntelligenceTAXIIResource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := azuresdkhacks.DataConnectorsClient{BaseClient: metadata.Client.Sentinel.DataConnectorsClient.BaseClient} + + var state DataConnectorThreatIntelligenceTAXIIModel + if err := metadata.Decode(&state); err != nil { + return err + } + + 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.(azuresdkhacks.TiTaxiiDataConnector) + if !ok { + return fmt.Errorf("%s was not an Threat Intelligence TAXII Data Connector", id) + } + + model := DataConnectorThreatIntelligenceTAXIIModel{ + Name: id.Name, + LogAnalyticsWorkspaceId: workspaceId.ID(), + UserName: state.UserName, // setting the user name from state, as it is not returned from API + Password: state.Password, // setting the password from state, as it is not returned from API + } + + if props := dc.TiTaxiiDataConnectorProperties; props != nil { + if props.FriendlyName != nil { + model.DisplayName = *props.FriendlyName + } + + if props.TaxiiServer != nil { + model.APIRootURL = *props.TaxiiServer + } + + if props.CollectionID != nil { + model.CollectionID = *props.CollectionID + } + + model.PollingFrequency = string(props.PollingFrequency) + + if props.TaxiiLookbackPeriod != nil { + model.LookbackDate = props.TaxiiLookbackPeriod.Format(time.RFC3339) + } + + if props.TenantID != nil { + model.TenantId = *props.TenantID + } + } + + return metadata.Encode(&model) + }, + } +} + +func (r DataConnectorThreatIntelligenceTAXIIResource) Update() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := azuresdkhacks.DataConnectorsClient{BaseClient: metadata.Client.Sentinel.DataConnectorsClient.BaseClient} + + id, err := parse.DataConnectorID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + var plan DataConnectorThreatIntelligenceTAXIIModel + if err := metadata.Decode(&plan); err != nil { + return err + } + + existing, err := client.Get(ctx, id.ResourceGroup, id.WorkspaceName, id.Name) + if err != nil { + return fmt.Errorf("retrieving %s: %+v", id, err) + } + + dc, ok := existing.Value.(azuresdkhacks.TiTaxiiDataConnector) + if !ok { + return fmt.Errorf("%s was not an Threat Intelligence TAXII Data Connector", id) + } + + if props := dc.TiTaxiiDataConnectorProperties; props != nil { + if metadata.ResourceData.HasChange("display_name") { + props.FriendlyName = &plan.DisplayName + } + if metadata.ResourceData.HasChange("api_root_url") { + props.TaxiiServer = &plan.APIRootURL + } + if metadata.ResourceData.HasChange("collection_id") { + props.CollectionID = &plan.CollectionID + } + if metadata.ResourceData.HasChange("user_name") { + props.UserName = &plan.UserName + } + if metadata.ResourceData.HasChange("password") { + props.Password = &plan.Password + } + if metadata.ResourceData.HasChange("polling_frequency") { + props.PollingFrequency = azuresdkhacks.PollingFrequency(plan.PollingFrequency) + } + if metadata.ResourceData.HasChange("lookback_date") { + // Format is guaranteed by schema validation + lookbackDate, _ := time.Parse(time.RFC3339, plan.LookbackDate) + props.TaxiiLookbackPeriod = &azuresdkhacks.Time{ + Time: lookbackDate, + } + } + + // Setting the user name and password if non empty in plan, which are required by the API. + if plan.UserName != "" { + props.UserName = &plan.UserName + } + if plan.Password != "" { + props.Password = &plan.Password + } + } + + if _, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.WorkspaceName, id.Name, dc); err != nil { + return fmt.Errorf("updating %s: %+v", id, err) + } + + return nil + }, + } +} + +func (r DataConnectorThreatIntelligenceTAXIIResource) 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 + }, + } +} diff --git a/internal/services/sentinel/sentinel_data_connector_threat_intelligence_taxii_test.go b/internal/services/sentinel/sentinel_data_connector_threat_intelligence_taxii_test.go new file mode 100644 index 000000000000..7945dbdc4482 --- /dev/null +++ b/internal/services/sentinel/sentinel_data_connector_threat_intelligence_taxii_test.go @@ -0,0 +1,229 @@ +package sentinel_test + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "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/azuresdkhacks" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/sentinel/parse" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type taxiiInfo struct { + APIRootURL string + CollectionID string + UserName string + Password string +} + +type DataConnectorThreatIntelligenceTAXIIResource struct { + taxiiInfo taxiiInfo + taxiiInfoAlt taxiiInfo +} + +func NewDataConnectorThreatIntelligenceTAXIIResource() DataConnectorThreatIntelligenceTAXIIResource { + return DataConnectorThreatIntelligenceTAXIIResource{ + taxiiInfo: taxiiInfo{ + APIRootURL: os.Getenv("ARM_TEST_TAXII_API_ROOT_URL"), + CollectionID: os.Getenv("ARM_TEST_TAXII_COLLECTION_ID"), + UserName: os.Getenv("ARM_TEST_TAXII_USERNAME"), + Password: os.Getenv("ARM_TEST_TAXII_PASSWORD"), + }, + taxiiInfoAlt: taxiiInfo{ + APIRootURL: os.Getenv("ARM_TEST_TAXII_API_ROOT_URL_ALT"), + CollectionID: os.Getenv("ARM_TEST_TAXII_COLLECTION_ID_ALT"), + UserName: os.Getenv("ARM_TEST_TAXII_USERNAME_ALT"), + Password: os.Getenv("ARM_TEST_TAXII_PASSWORD_ALT"), + }, + } +} + +func (r DataConnectorThreatIntelligenceTAXIIResource) preCheck(t *testing.T, forUpdate bool) { + if r.taxiiInfo.APIRootURL == "" { + t.Skipf(`"ARM_TEST_TAXII_API_ROOT_URL" not specified`) + } + if r.taxiiInfo.APIRootURL == "" { + t.Skipf(`"ARM_TEST_TAXII_COLLECTION_ID" not specified`) + } + if forUpdate { + if r.taxiiInfoAlt.APIRootURL == "" { + t.Skipf(`"ARM_TEST_TAXII_API_ROOT_URL_ALT" not specified`) + } + if r.taxiiInfoAlt.CollectionID == "" { + t.Skipf(`"ARM_TEST_TAXII_COLLECTION_ID_ALT" not specified`) + } + } +} + +func TestAccDataConnectorThreatIntelligenceTAXII_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_sentinel_data_connector_threat_intelligence_taxii", "test") + r := NewDataConnectorThreatIntelligenceTAXIIResource() + r.preCheck(t, false) + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.basic(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("user_name", "password"), + }) +} + +func TestAccDataConnectorThreatIntelligenceTAXII_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_sentinel_data_connector_threat_intelligence_taxii", "test") + r := NewDataConnectorThreatIntelligenceTAXIIResource() + r.preCheck(t, true) + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.basic(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("user_name", "password"), + { + Config: r.update(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("user_name", "password"), + { + Config: r.basic(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("user_name", "password"), + }) +} + +func TestAccDataConnectorThreatIntelligenceTAXII_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_sentinel_data_connector_threat_intelligence_taxii", "test") + r := NewDataConnectorThreatIntelligenceTAXIIResource() + r.preCheck(t, false) + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.basic(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func (r DataConnectorThreatIntelligenceTAXIIResource) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) { + client := azuresdkhacks.DataConnectorsClient{BaseClient: clients.Sentinel.DataConnectorsClient.BaseClient} + + 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 DataConnectorThreatIntelligenceTAXIIResource) basic(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_sentinel_data_connector_threat_intelligence_taxii" "test" { + name = "acctestDC-%d" + log_analytics_workspace_id = azurerm_log_analytics_workspace.test.id + display_name = "test" + api_root_url = "%s" + collection_id = "%s" + user_name = "%s" + password = "%s" + depends_on = [azurerm_log_analytics_solution.test] +} +`, template, data.RandomInteger, r.taxiiInfo.APIRootURL, r.taxiiInfo.CollectionID, r.taxiiInfo.UserName, r.taxiiInfo.Password) +} + +func (r DataConnectorThreatIntelligenceTAXIIResource) update(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_sentinel_data_connector_threat_intelligence_taxii" "test" { + name = "acctestDC-%d" + log_analytics_workspace_id = azurerm_log_analytics_workspace.test.id + display_name = "test_update" + api_root_url = "%s" + collection_id = "%s" + user_name = "%s" + password = "%s" + polling_frequency = "OnceADay" + lookback_date = "1990-01-01T00:00:00Z" + depends_on = [azurerm_log_analytics_solution.test] +} +`, template, data.RandomInteger, r.taxiiInfoAlt.APIRootURL, r.taxiiInfoAlt.CollectionID, r.taxiiInfoAlt.UserName, r.taxiiInfoAlt.Password) +} + +func (r DataConnectorThreatIntelligenceTAXIIResource) requiresImport(data acceptance.TestData) string { + template := r.basic(data) + return fmt.Sprintf(` +%s + +resource "azurerm_sentinel_data_connector_threat_intelligence_taxii" "import" { + name = azurerm_sentinel_data_connector_threat_intelligence_taxii.test.name + log_analytics_workspace_id = azurerm_sentinel_data_connector_threat_intelligence_taxii.test.log_analytics_workspace_id + display_name = azurerm_sentinel_data_connector_threat_intelligence_taxii.test.display_name + api_root_url = azurerm_sentinel_data_connector_threat_intelligence_taxii.test.api_root_url + collection_id = azurerm_sentinel_data_connector_threat_intelligence_taxii.test.collection_id +} +`, template) +} + +func (r DataConnectorThreatIntelligenceTAXIIResource) 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) +} diff --git a/website/docs/r/sentinel_data_connector_threat_intelligence_taxii.html.markdown b/website/docs/r/sentinel_data_connector_threat_intelligence_taxii.html.markdown new file mode 100644 index 000000000000..e7da7b5d6b8d --- /dev/null +++ b/website/docs/r/sentinel_data_connector_threat_intelligence_taxii.html.markdown @@ -0,0 +1,99 @@ +--- +subcategory: "Sentinel" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_sentinel_data_connector_threat_intelligence_taxii" +description: |- + Manages an Threat Intelligence TAXII Data Connector. +--- + +# azurerm_sentinel_data_connector_threat_intelligence_taxii + +Manages an Threat Intelligence TAXII 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_threat_intelligence_taxii" "example" { + name = "example" + log_analytics_workspace_id = azurerm_log_analytics_solution.example.workspace_resource_id + display_name = "example" + api_root_url = "https://foo/taxii2/api2/" + collection_id = "someid" + depends_on = [azurerm_log_analytics_solution.test] +} +``` + +## Arguments Reference + +The following arguments are supported: + +* `log_analytics_workspace_id` - (Required) The ID of the Log Analytics Workspace that this Threat Intelligence TAXII Data Connector resides in. Changing this forces a new Threat Intelligence TAXII Data Connector to be created. + +* `name` - (Required) The name which should be used for this Threat Intelligence TAXII Data Connector. Changing this forces a new Threat Intelligence TAXII Data Connector to be created. + +* `display_name` - (Required) The friendly name which should be used for this Threat Intelligence TAXII Data Connector. + +* `api_root_url` - (Required) The API root URI of the TAXII server. + +* `collection_id` - (Required) The collection ID of the TAXII server. + +* `user_name` - (Optional) The user name for the TAXII server. + +* `password` - (Optional) The password for the TAXII server. + +* `polling_frequency` - (Optional) The polling frequency for the TAXII server. Possible values are `OnceAMinute`, `OnceAnHour` and `OnceADay`. Defaults to `OnceAnHour`. + +* `lookback_date` - (Optional) The lookback date for the TAXII server in RFC3339. Defaults to `1970-01-01T00:00:00Z`. + +--- + +* `tenant_id` - (Optional) The ID of the tenant that this Threat Intelligence TAXII Data Connector connects to. Changing this forces a new Threat Intelligence TAXII Data Connector to be created. + +-> **NOTE** Currently, only the same tenant as the running account is allowed. Cross-tenant scenario is not supported yet. + +## Attributes Reference + +In addition to the Arguments listed above - the following Attributes are exported: + +* `id` - The ID of the Threat Intelligence TAXII Data Connector. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/language/resources/syntax#operation-timeouts) for certain actions: + +* `create` - (Defaults to 30 minutes) Used when creating the Threat Intelligence TAXII Data Connector. +* `read` - (Defaults to 5 minutes) Used when retrieving the Threat Intelligence TAXII Data Connector. +* `delete` - (Defaults to 30 minutes) Used when deleting the Threat Intelligence TAXII Data Connector. + +## Import + +Threat Intelligence TAXII Data Connectors can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_sentinel_data_connector_threat_intelligence_taxii.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.OperationalInsights/workspaces/workspace1/providers/Microsoft.SecurityInsights/dataConnectors/dc1 +```