diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt
index 1d3e45d..e9d82a9 100644
--- a/.github/actions/spelling/expect.txt
+++ b/.github/actions/spelling/expect.txt
@@ -15,6 +15,7 @@ Hmj
JFB
JFUz
Jhb
+jira
KBp
ljq
LQV
diff --git a/docs/resources/integration_email.md b/docs/resources/integration_email.md
new file mode 100644
index 0000000..f6649a2
--- /dev/null
+++ b/docs/resources/integration_email.md
@@ -0,0 +1,73 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "mondoo_integration_email Resource - terraform-provider-mondoo"
+subcategory: ""
+description: |-
+ Send an email to your ticket system, or any recipient.
+---
+
+# mondoo_integration_email (Resource)
+
+Send an email to your ticket system, or any recipient.
+
+## Example Usage
+
+```terraform
+provider "mondoo" {
+ space = "hungry-poet-123456"
+}
+
+# Setup the Email integration
+resource "mondoo_integration_email" "email_integration" {
+ name = "My Email Integration"
+
+ recipients = [
+ {
+ name = "John Doe"
+ email = "john@example.com"
+ is_default = true
+ reference_url = "https://example.com"
+ },
+ {
+ name = "Alice Doe"
+ email = "alice@example.com"
+ is_default = false
+ reference_url = "https://example.com"
+ }
+ ]
+
+ auto_create = true
+ auto_close = true
+}
+```
+
+
+## Schema
+
+### Required
+
+- `name` (String) Name of the integration.
+- `recipients` (Attributes List) List of email recipients. (see [below for nested schema](#nestedatt--recipients))
+
+### Optional
+
+- `auto_close` (Boolean) Auto close tickets (defaults to false).
+- `auto_create` (Boolean) Auto create tickets (defaults to false).
+- `space_id` (String) Mondoo Space Identifier. If it is not provided, the provider space is used.
+
+### Read-Only
+
+- `mrn` (String) Integration identifier
+
+
+### Nested Schema for `recipients`
+
+Required:
+
+- `email` (String) Recipient email address.
+- `name` (String) Recipient name.
+
+Optional:
+
+- `is_default` (Boolean) Mark this recipient as default. This needs to be set if auto_create is enabled.
+- `reference_url` (String) Reference URL for the recipient.
diff --git a/docs/resources/integration_jira.md b/docs/resources/integration_jira.md
new file mode 100644
index 0000000..a6f0f17
--- /dev/null
+++ b/docs/resources/integration_jira.md
@@ -0,0 +1,68 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "mondoo_integration_jira Resource - terraform-provider-mondoo"
+subcategory: ""
+description: |-
+ Integrate the Ticketing System Jira with Mondoo to automatically create and close issues based on Mondoo findings.
+---
+
+# mondoo_integration_jira (Resource)
+
+Integrate the Ticketing System Jira with Mondoo to automatically create and close issues based on Mondoo findings.
+
+## Example Usage
+
+```terraform
+variable "jira_token" {
+ description = "The Jira API Token"
+ type = string
+ sensitive = true
+}
+
+provider "mondoo" {
+ space = "hungry-poet-123456"
+}
+
+# Setup the Jira integration
+resource "mondoo_integration_jira" "jira_integration" {
+ name = "My Jira Integration"
+ host = "https://your-instance.atlassian.net"
+ email = "jira.owner@email.com"
+ # default_project = "MONDOO"
+
+ auto_create = true
+ auto_close = true
+
+ credentials = {
+ token = var.jira_token
+ }
+}
+```
+
+
+## Schema
+
+### Required
+
+- `credentials` (Attributes) (see [below for nested schema](#nestedatt--credentials))
+- `email` (String) Jira user email.
+- `host` (String) Jira host URL.
+- `name` (String) Name of the integration.
+
+### Optional
+
+- `auto_close` (Boolean) Automatically close Jira issues for resolved Mondoo findings
+- `auto_create` (Boolean) Automatically create Jira issues for Mondoo findings.
+- `default_project` (String) Default Jira project (is represented by the project key e.g. `MONDOO`).
+- `space_id` (String) Mondoo Space Identifier. If it is not provided, the provider space is used.
+
+### Read-Only
+
+- `mrn` (String) Integration identifier.
+
+
+### Nested Schema for `credentials`
+
+Required:
+
+- `token` (String, Sensitive) Jira API token.
diff --git a/examples/resources/mondoo_integration_email/main.tf b/examples/resources/mondoo_integration_email/main.tf
new file mode 100644
index 0000000..24d24a1
--- /dev/null
+++ b/examples/resources/mondoo_integration_email/main.tf
@@ -0,0 +1,8 @@
+terraform {
+ required_providers {
+ mondoo = {
+ source = "mondoohq/mondoo"
+ version = ">= 0.19"
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/resources/mondoo_integration_email/resource.tf b/examples/resources/mondoo_integration_email/resource.tf
new file mode 100644
index 0000000..17b0727
--- /dev/null
+++ b/examples/resources/mondoo_integration_email/resource.tf
@@ -0,0 +1,26 @@
+provider "mondoo" {
+ space = "hungry-poet-123456"
+}
+
+# Setup the Email integration
+resource "mondoo_integration_email" "email_integration" {
+ name = "My Email Integration"
+
+ recipients = [
+ {
+ name = "John Doe"
+ email = "john@example.com"
+ is_default = true
+ reference_url = "https://example.com"
+ },
+ {
+ name = "Alice Doe"
+ email = "alice@example.com"
+ is_default = false
+ reference_url = "https://example.com"
+ }
+ ]
+
+ auto_create = true
+ auto_close = true
+}
diff --git a/examples/resources/mondoo_integration_jira/main.tf b/examples/resources/mondoo_integration_jira/main.tf
new file mode 100644
index 0000000..24d24a1
--- /dev/null
+++ b/examples/resources/mondoo_integration_jira/main.tf
@@ -0,0 +1,8 @@
+terraform {
+ required_providers {
+ mondoo = {
+ source = "mondoohq/mondoo"
+ version = ">= 0.19"
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/resources/mondoo_integration_jira/resource.tf b/examples/resources/mondoo_integration_jira/resource.tf
new file mode 100644
index 0000000..de60f4e
--- /dev/null
+++ b/examples/resources/mondoo_integration_jira/resource.tf
@@ -0,0 +1,24 @@
+variable "jira_token" {
+ description = "The Jira API Token"
+ type = string
+ sensitive = true
+}
+
+provider "mondoo" {
+ space = "hungry-poet-123456"
+}
+
+# Setup the Jira integration
+resource "mondoo_integration_jira" "jira_integration" {
+ name = "My Jira Integration"
+ host = "https://your-instance.atlassian.net"
+ email = "jira.owner@email.com"
+ # default_project = "MONDOO"
+
+ auto_create = true
+ auto_close = true
+
+ credentials = {
+ token = var.jira_token
+ }
+}
diff --git a/go.mod b/go.mod
index 3c0369e..d5902c1 100644
--- a/go.mod
+++ b/go.mod
@@ -13,7 +13,7 @@ require (
github.com/hashicorp/terraform-plugin-log v0.9.0
github.com/hashicorp/terraform-plugin-testing v1.11.0
github.com/stretchr/testify v1.10.0
- go.mondoo.com/cnquery/v11 v11.33.0
+ go.mondoo.com/cnquery/v11 v11.33.1
go.mondoo.com/mondoo-go v0.0.0-20241118222255-5299c9adc97c
gopkg.in/yaml.v2 v2.4.0
)
diff --git a/go.sum b/go.sum
index d0dc833..513e48c 100644
--- a/go.sum
+++ b/go.sum
@@ -605,8 +605,8 @@ go.abhg.dev/goldmark/frontmatter v0.2.0/go.mod h1:XqrEkZuM57djk7zrlRUB02x8I5J0px
go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=
go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY=
-go.mondoo.com/cnquery/v11 v11.33.0 h1:lXLEPwt+7D3GW2hKMNmHlQlD6YhEd3izGnmHIo2a3Kg=
-go.mondoo.com/cnquery/v11 v11.33.0/go.mod h1:ynuOojMFVuwUAq7nC0Dk6Ut/2MS9T/R+hHmWQdP491Q=
+go.mondoo.com/cnquery/v11 v11.33.1 h1:ppAKcz3PG80AXKclDYOR3w89B7HgszDw3OSStJj4CcI=
+go.mondoo.com/cnquery/v11 v11.33.1/go.mod h1:ynuOojMFVuwUAq7nC0Dk6Ut/2MS9T/R+hHmWQdP491Q=
go.mondoo.com/mondoo-go v0.0.0-20241118222255-5299c9adc97c h1:0u12icLFjeTLzNQHjPs8Mw65VG1Wl8LxHoGRihwaSmg=
go.mondoo.com/mondoo-go v0.0.0-20241118222255-5299c9adc97c/go.mod h1:VTTbqYTjin1hKSnwKHVYeOTEyJrAZarNlf1I8M2rlpM=
go.mondoo.com/ranger-rpc v0.6.4 h1:q01kjESvF2HSnbFO+TjpUQSiI2IM8JWGJLH3u0vNxZA=
diff --git a/internal/provider/gql.go b/internal/provider/gql.go
index 04881aa..26a721b 100644
--- a/internal/provider/gql.go
+++ b/internal/provider/gql.go
@@ -617,6 +617,27 @@ type ShodanConfigurationOptions struct {
Targets []string
}
+type JiraConfigurationOptions struct {
+ Host string
+ Email string
+ DefaultProject string
+ AutoCloseTickets bool
+ AutoCreateCases bool
+}
+
+type EmailConfigurationOptions struct {
+ Recipients []EmailRecipient
+ AutoCreateTickets bool
+ AutoCloseTickets bool
+}
+
+type EmailRecipient struct {
+ Name string
+ Email string
+ IsDefault bool
+ ReferenceURL string
+}
+
type ClientIntegrationConfigurationOptions struct {
AzureConfigurationOptions AzureConfigurationOptions `graphql:"... on AzureConfigurationOptions"`
HostConfigurationOptions HostConfigurationOptions `graphql:"... on HostConfigurationOptions"`
@@ -626,6 +647,8 @@ type ClientIntegrationConfigurationOptions struct {
GithubConfigurationOptions GithubConfigurationOptions `graphql:"... on GithubConfigurationOptions"`
HostedAwsConfigurationOptions HostedAwsConfigurationOptions `graphql:"... on HostedAwsConfigurationOptions"`
ShodanConfigurationOptions ShodanConfigurationOptions `graphql:"... on ShodanConfigurationOptions"`
+ JiraConfigurationOptions JiraConfigurationOptions `graphql:"... on JiraConfigurationOptions"`
+ EmailConfigurationOptions EmailConfigurationOptions `graphql:"... on EmailConfigurationOptions"`
GitlabConfigurationOptions GitlabConfigurationOptions `graphql:"... on GitlabConfigurationOptions"`
// Add other configuration options here
}
diff --git a/internal/provider/integration_email_resource.go b/internal/provider/integration_email_resource.go
new file mode 100644
index 0000000..2e7c50b
--- /dev/null
+++ b/internal/provider/integration_email_resource.go
@@ -0,0 +1,450 @@
+package provider
+
+import (
+ "context"
+ "fmt"
+ "regexp"
+
+ "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
+ "github.com/hashicorp/terraform-plugin-framework/path"
+ "github.com/hashicorp/terraform-plugin-framework/resource"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/schema/validator"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/hashicorp/terraform-plugin-framework/types/basetypes"
+ "github.com/hashicorp/terraform-plugin-log/tflog"
+ mondoov1 "go.mondoo.com/mondoo-go"
+)
+
+var _ resource.Resource = (*integrationEmailResource)(nil)
+
+func NewIntegrationEmailResource() resource.Resource {
+ return &integrationEmailResource{}
+}
+
+type integrationEmailResource struct {
+ client *ExtendedGqlClient
+}
+
+type integrationEmailResourceModel struct {
+ // scope
+ SpaceID types.String `tfsdk:"space_id"`
+
+ // integration details
+ Mrn types.String `tfsdk:"mrn"`
+ Name types.String `tfsdk:"name"`
+ Recipients *[]integrationEmailRecipientInput `tfsdk:"recipients"`
+ AutoCreateTickets types.Bool `tfsdk:"auto_create"`
+ AutoCloseTickets types.Bool `tfsdk:"auto_close"`
+}
+
+type integrationEmailRecipientInput struct {
+ Name types.String `tfsdk:"name"`
+ Email types.String `tfsdk:"email"`
+ IsDefault types.Bool `tfsdk:"is_default"`
+ ReferenceURL types.String `tfsdk:"reference_url"`
+}
+
+func (m integrationEmailResourceModel) GetConfigurationOptions() *mondoov1.EmailConfigurationOptionsInput {
+ opts := &mondoov1.EmailConfigurationOptionsInput{
+ Recipients: convertRecipients(m.Recipients),
+ AutoCreateTickets: mondoov1.NewBooleanPtr(mondoov1.Boolean(m.AutoCreateTickets.ValueBool())),
+ AutoCloseTickets: mondoov1.NewBooleanPtr(mondoov1.Boolean(m.AutoCloseTickets.ValueBool())),
+ }
+
+ return opts
+}
+
+func (r *integrationEmailResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
+ resp.TypeName = req.ProviderTypeName + "_integration_email"
+}
+
+type defaultRecipientValidator struct{}
+
+func (d defaultRecipientValidator) ValidateList(ctx context.Context, req validator.ListRequest, resp *validator.ListResponse) {
+ // Return early if value is null or unknown
+ if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() {
+ return
+ }
+
+ // Convert ListValue to a slice of ObjectValues
+ var recipients []basetypes.ObjectValue
+ diags := req.ConfigValue.ElementsAs(ctx, &recipients, false)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+
+ defaultCount := 0
+
+ // Iterate over recipients to count default ones
+ for _, recipient := range recipients {
+ // Access the attributes of the recipient
+ attrs := recipient.Attributes()
+
+ // Retrieve the "is_default" attribute
+ isDefaultAttr, exists := attrs["is_default"]
+ if !exists {
+ resp.Diagnostics.AddError(
+ "Missing Attribute",
+ "Recipient object is missing the 'is_default' attribute.",
+ )
+ return
+ }
+
+ // Check if the value is true
+ isDefault, ok := isDefaultAttr.(types.Bool)
+ if !ok {
+ resp.Diagnostics.AddError(
+ "Invalid Attribute Type",
+ "The 'is_default' attribute must be a boolean.",
+ )
+ return
+ }
+
+ if isDefault.ValueBool() {
+ defaultCount++
+ }
+ }
+
+ // Validate that only one recipient is marked as default
+ if defaultCount > 1 {
+ resp.Diagnostics.AddError(
+ "Too Many Default Recipients",
+ "Only one recipient can be marked as default.",
+ )
+ }
+}
+
+func (d defaultRecipientValidator) Description(ctx context.Context) string {
+ return "Ensures that only one recipient is marked as default."
+}
+
+func (d defaultRecipientValidator) MarkdownDescription(ctx context.Context) string {
+ return "Ensures that only one recipient is marked as `default`."
+}
+
+func NewDefaultRecipientValidator() validator.List {
+ return defaultRecipientValidator{}
+}
+
+type AutoCreateValidator struct{}
+
+func (v AutoCreateValidator) ValidateBool(ctx context.Context, req validator.BoolRequest, resp *validator.BoolResponse) {
+ if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() {
+ return
+ }
+
+ autoCreate := req.ConfigValue.ValueBool()
+ if !autoCreate {
+ return
+ }
+
+ // Retrieve the recipients list from configuration
+ var recipientsAttr basetypes.ListValue
+ recipientsDiags := req.Config.GetAttribute(ctx, path.Root("recipients"), &recipientsAttr)
+ if recipientsDiags.HasError() {
+ resp.Diagnostics.Append(recipientsDiags...)
+ return
+ }
+
+ var recipients []basetypes.ObjectValue
+ diags := recipientsAttr.ElementsAs(ctx, &recipients, false)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+
+ defaultFound := false
+ for _, recipient := range recipients {
+ attrs := recipient.Attributes()
+
+ isDefaultAttr, exists := attrs["is_default"]
+ if exists {
+ isDefault, ok := isDefaultAttr.(types.Bool)
+ if !ok {
+ resp.Diagnostics.AddError(
+ "Invalid Attribute Type",
+ "The 'is_default' attribute must be a boolean.",
+ )
+ return
+ }
+ if isDefault.ValueBool() {
+ defaultFound = true
+ break
+ }
+ }
+ }
+
+ if !defaultFound {
+ resp.Diagnostics.AddError(
+ "Missing Default Recipient",
+ "At least one recipient must be marked as default when auto-create is enabled.",
+ )
+ }
+}
+
+func (v AutoCreateValidator) Description(ctx context.Context) string {
+ return "Ensures that at least one recipient is marked as default when auto-create is enabled."
+}
+
+func (v AutoCreateValidator) MarkdownDescription(ctx context.Context) string {
+ return "Ensures that at least one recipient is marked as `default` when auto-create is enabled."
+}
+
+func NewAutoCreateValidator() validator.Bool {
+ return AutoCreateValidator{}
+}
+
+func (r *integrationEmailResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
+ resp.Schema = schema.Schema{
+ MarkdownDescription: "Send an email to your ticket system, or any recipient.",
+ Attributes: map[string]schema.Attribute{
+ "space_id": schema.StringAttribute{
+ MarkdownDescription: "Mondoo Space Identifier. If it is not provided, the provider space is used.",
+ Optional: true,
+ Computed: true,
+ PlanModifiers: []planmodifier.String{
+ stringplanmodifier.UseStateForUnknown(),
+ },
+ },
+ "mrn": schema.StringAttribute{
+ Computed: true,
+ MarkdownDescription: "Integration identifier",
+ PlanModifiers: []planmodifier.String{
+ stringplanmodifier.UseStateForUnknown(),
+ },
+ },
+ "name": schema.StringAttribute{
+ MarkdownDescription: "Name of the integration.",
+ Required: true,
+ Validators: []validator.String{
+ stringvalidator.LengthAtMost(250),
+ },
+ },
+ "recipients": schema.ListNestedAttribute{
+ MarkdownDescription: "List of email recipients.",
+ Required: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "name": schema.StringAttribute{
+ MarkdownDescription: "Recipient name.",
+ Required: true,
+ },
+ "email": schema.StringAttribute{
+ MarkdownDescription: "Recipient email address.",
+ Required: true,
+ Validators: []validator.String{
+ stringvalidator.RegexMatches(
+ regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`),
+ "must be a valid email",
+ ),
+ },
+ },
+ "is_default": schema.BoolAttribute{
+ MarkdownDescription: "Mark this recipient as default. This needs to be set if auto_create is enabled.",
+ Optional: true,
+ },
+ "reference_url": schema.StringAttribute{
+ MarkdownDescription: "Reference URL for the recipient.",
+ Optional: true,
+ },
+ },
+ },
+ Validators: []validator.List{
+ NewDefaultRecipientValidator(),
+ },
+ },
+ "auto_create": schema.BoolAttribute{
+ MarkdownDescription: "Auto create tickets (defaults to false).",
+ Optional: true,
+ Validators: []validator.Bool{
+ NewAutoCreateValidator(),
+ },
+ },
+ "auto_close": schema.BoolAttribute{
+ MarkdownDescription: "Auto close tickets (defaults to false).",
+ Optional: true,
+ },
+ },
+ }
+}
+
+func (r *integrationEmailResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
+ // Prevent panic if the provider has not been configured.
+ if req.ProviderData == nil {
+ return
+ }
+
+ client, ok := req.ProviderData.(*ExtendedGqlClient)
+
+ if !ok {
+ resp.Diagnostics.AddError(
+ "Unexpected Resource Configure Type",
+ fmt.Sprintf("Expected *http.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
+ )
+
+ return
+ }
+
+ r.client = client
+}
+
+func convertRecipients(recipients *[]integrationEmailRecipientInput) []mondoov1.EmailRecipientInput {
+ if recipients == nil {
+ return nil
+ }
+ var result []mondoov1.EmailRecipientInput
+ for _, r := range *recipients {
+ result = append(result, mondoov1.EmailRecipientInput{
+ Name: mondoov1.String(r.Name.ValueString()),
+ Email: mondoov1.String(r.Email.ValueString()),
+ IsDefault: mondoov1.Boolean(r.IsDefault.ValueBool()),
+ ReferenceURL: mondoov1.NewStringPtr(mondoov1.String(r.ReferenceURL.ValueString())),
+ })
+ }
+ return result
+}
+
+func (r *integrationEmailResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
+ var data integrationEmailResourceModel
+
+ // Read Terraform plan data into the model
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
+
+ if resp.Diagnostics.HasError() {
+ return
+ }
+ // Compute and validate the space
+ space, err := r.client.ComputeSpace(data.SpaceID)
+ if err != nil {
+ resp.Diagnostics.AddError("Invalid Configuration", err.Error())
+ return
+ }
+ ctx = tflog.SetField(ctx, "space_mrn", space.MRN())
+
+ // Do GraphQL request to API to create the resource.
+ tflog.Debug(ctx, "Creating integration")
+ integration, err := r.client.CreateIntegration(ctx,
+ space.MRN(),
+ data.Name.ValueString(),
+ mondoov1.ClientIntegrationTypeTicketSystemEmail,
+ mondoov1.ClientIntegrationConfigurationInput{
+ EmailConfigurationOptions: data.GetConfigurationOptions(),
+ })
+ if err != nil {
+ resp.Diagnostics.
+ AddError("Client Error",
+ fmt.Sprintf("Unable to create Email integration, got error: %s", err),
+ )
+ return
+ }
+
+ // Save space mrn into the Terraform state.
+ data.Mrn = types.StringValue(string(integration.Mrn))
+ data.Name = types.StringValue(string(integration.Name))
+ data.SpaceID = types.StringValue(space.ID())
+
+ // Save data into Terraform state
+ resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
+
+func (r *integrationEmailResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
+ var data integrationEmailResourceModel
+
+ // Read Terraform prior state data into the model
+ resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
+
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ // Read API call logic
+
+ // Save updated data into Terraform state
+ resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
+
+func (r *integrationEmailResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
+ var data integrationEmailResourceModel
+
+ // Read Terraform plan data into the model
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
+
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ // Do GraphQL request to API to update the resource.
+ opts := mondoov1.ClientIntegrationConfigurationInput{
+ EmailConfigurationOptions: data.GetConfigurationOptions(),
+ }
+
+ _, err := r.client.UpdateIntegration(ctx,
+ data.Mrn.ValueString(),
+ data.Name.ValueString(),
+ mondoov1.ClientIntegrationTypeTicketSystemEmail,
+ opts,
+ )
+ if err != nil {
+ resp.Diagnostics.
+ AddError("Client Error",
+ fmt.Sprintf("Unable to update Email integration, got error: %s", err),
+ )
+ return
+ }
+
+ // Save updated data into Terraform state
+ resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
+
+func (r *integrationEmailResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
+ var data integrationEmailResourceModel
+
+ // Read Terraform prior state data into the model
+ resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
+
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ // Do GraphQL request to API to update the resource.
+ _, err := r.client.DeleteIntegration(ctx, data.Mrn.ValueString())
+ if err != nil {
+ resp.Diagnostics.
+ AddError("Client Error",
+ fmt.Sprintf("Unable to delete Email integration, got error: %s", err),
+ )
+ return
+ }
+}
+
+func (r *integrationEmailResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
+ integration, ok := r.client.ImportIntegration(ctx, req, resp)
+ if !ok {
+ return
+ }
+
+ var recipients []integrationEmailRecipientInput
+ for _, recipient := range integration.ConfigurationOptions.EmailConfigurationOptions.Recipients {
+ recipients = append(recipients, integrationEmailRecipientInput{
+ Name: types.StringValue(recipient.Name),
+ Email: types.StringValue(recipient.Email),
+ IsDefault: types.BoolValue(recipient.IsDefault),
+ ReferenceURL: types.StringValue(recipient.ReferenceURL),
+ })
+ }
+
+ model := integrationEmailResourceModel{
+ Mrn: types.StringValue(integration.Mrn),
+ Name: types.StringValue(integration.Name),
+ SpaceID: types.StringValue(integration.SpaceID()),
+ AutoCreateTickets: types.BoolValue(integration.ConfigurationOptions.EmailConfigurationOptions.AutoCreateTickets),
+ AutoCloseTickets: types.BoolValue(integration.ConfigurationOptions.EmailConfigurationOptions.AutoCloseTickets),
+ Recipients: &recipients,
+ }
+
+ resp.State.Set(ctx, &model)
+}
diff --git a/internal/provider/integration_email_resource_test.go b/internal/provider/integration_email_resource_test.go
new file mode 100644
index 0000000..d9ca9bf
--- /dev/null
+++ b/internal/provider/integration_email_resource_test.go
@@ -0,0 +1,102 @@
+// Copyright (c) Mondoo, Inc.
+// SPDX-License-Identifier: BUSL-1.1
+
+package provider
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+)
+
+func TestAccEmailIntegrationResource(t *testing.T) {
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
+ Steps: []resource.TestStep{
+ // Create and Read testing
+ {
+ Config: testAccEmailIntegrationResourceConfig(accSpace.ID(), "one", `[
+ {"name": "John Doe", "email": "john@example.com", "is_default": true, "reference_url": "https://example.com"},
+ {"name": "Alice Doe", "email": "alice@example.com", "is_default": false, "reference_url": "https://example.com"}
+ ]`),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("mondoo_integration_email.test", "name", "one"),
+ resource.TestCheckResourceAttr("mondoo_integration_email.test", "space_id", accSpace.ID()),
+ resource.TestCheckResourceAttr("mondoo_integration_email.test", "recipients.0.name", "John Doe"),
+ ),
+ },
+ {
+ Config: testAccEmailIntegrationResourceWithSpaceInProviderConfig(accSpace.ID(), "two", true, true),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("mondoo_integration_email.test", "name", "two"),
+ resource.TestCheckResourceAttr("mondoo_integration_email.test", "space_id", accSpace.ID()),
+ resource.TestCheckResourceAttr("mondoo_integration_email.test", "auto_create", "true"),
+ resource.TestCheckResourceAttr("mondoo_integration_email.test", "auto_close", "true"),
+ ),
+ },
+ // Update and Read testing
+ {
+ Config: testAccEmailIntegrationResourceConfig(accSpace.ID(), "three", `[
+ {"name": "John Doe", "email": "john.doe@example.com", "is_default": true, "reference_url": "https://newurl.com"},
+ {"name": "Alice Doe", "email": "alice.doe@example.com", "is_default": false, "reference_url": "https://newurl.com"}
+ ]`),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("mondoo_integration_email.test", "name", "three"),
+ resource.TestCheckResourceAttr("mondoo_integration_email.test", "space_id", accSpace.ID()),
+ resource.TestCheckResourceAttr("mondoo_integration_email.test", "recipients.0.reference_url", "https://newurl.com"),
+ ),
+ },
+ {
+ Config: testAccEmailIntegrationResourceWithSpaceInProviderConfig(accSpace.ID(), "four", false, false),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("mondoo_integration_email.test", "name", "four"),
+ resource.TestCheckResourceAttr("mondoo_integration_email.test", "space_id", accSpace.ID()),
+ resource.TestCheckResourceAttr("mondoo_integration_email.test", "auto_create", "false"),
+ resource.TestCheckResourceAttr("mondoo_integration_email.test", "auto_close", "false"),
+ ),
+ },
+ // Delete testing automatically occurs in TestCase
+ },
+ })
+}
+
+func testAccEmailIntegrationResourceConfig(spaceID, intName, recipients string) string {
+ return fmt.Sprintf(`
+resource "mondoo_integration_email" "test" {
+ space_id = %[1]q
+ name = %[2]q
+ recipients = %[3]s
+ auto_create = true
+ auto_close = true
+}
+`, spaceID, intName, recipients)
+}
+
+func testAccEmailIntegrationResourceWithSpaceInProviderConfig(spaceID, intName string, autoCreate, autoClose bool) string {
+ return fmt.Sprintf(`
+provider "mondoo" {
+ space = %[1]q
+}
+resource "mondoo_integration_email" "test" {
+ name = %[2]q
+ recipients = [
+ {
+ name = "John Doe"
+ email = "john@example.com"
+ is_default = true
+ reference_url = "https://example.com"
+ },
+ {
+ name = "Alice Doe"
+ email = "alice@example.com"
+ is_default = false
+ reference_url = "https://example.com"
+ }
+ ]
+ auto_create = %[3]t
+ auto_close = %[4]t
+}
+`, spaceID, intName, autoCreate, autoClose)
+}
diff --git a/internal/provider/integration_gitlab_resource_test.go b/internal/provider/integration_gitlab_resource_test.go
new file mode 100644
index 0000000..04ddee1
--- /dev/null
+++ b/internal/provider/integration_gitlab_resource_test.go
@@ -0,0 +1,105 @@
+// Copyright (c) Mondoo, Inc.
+// SPDX-License-Identifier: BUSL-1.1
+
+package provider
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+)
+
+func TestAccGitLabResource(t *testing.T) {
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
+ Steps: []resource.TestStep{
+ // Create and Read testing
+ {
+ Config: testAccGitLabResourceConfig(accSpace.ID(), "one", true),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("mondoo_integration_gitlab.test", "name", "one"),
+ resource.TestCheckResourceAttr("mondoo_integration_gitlab.test", "space_id", accSpace.ID()),
+ resource.TestCheckResourceAttr("mondoo_integration_gitlab.test", "discovery.groups", "true"),
+ ),
+ },
+ {
+ Config: testAccGitLabResourceWithSpaceInProviderConfig(accSpace.ID(), "two", "abctoken12345"),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("mondoo_integration_gitlab.test", "name", "two"),
+ resource.TestCheckResourceAttr("mondoo_integration_gitlab.test", "space_id", accSpace.ID()),
+ resource.TestCheckResourceAttr("mondoo_integration_gitlab.test", "credentials.token", "abctoken12345"),
+ ),
+ },
+ // ImportState testing
+ // @afiune this doesn't work since most of our resources doesn't have the `id` attribute
+ // if we add it, instead of the `mrn` or as a copy, this import test will work
+ // {
+ // ResourceName: "mondoo_integration_gitlab.test",
+ // ImportState: true,
+ // ImportStateVerify: true,
+ // },
+ // Update and Read testing
+ {
+ Config: testAccGitLabResourceConfig(accSpace.ID(), "one", true),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("mondoo_integration_gitlab.test", "name", "one"),
+ resource.TestCheckResourceAttr("mondoo_integration_gitlab.test", "space_id", accSpace.ID()),
+ resource.TestCheckResourceAttr("mondoo_integration_gitlab.test", "discovery.groups", "true"),
+ ),
+ },
+ {
+ Config: testAccGitLabResourceWithSpaceInProviderConfig(accSpace.ID(), "two", "abctoken12345"),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("mondoo_integration_gitlab.test", "name", "two"),
+ resource.TestCheckResourceAttr("mondoo_integration_gitlab.test", "space_id", accSpace.ID()),
+ resource.TestCheckResourceAttr("mondoo_integration_gitlab.test", "credentials.token", "abctoken12345"),
+ ),
+ },
+ // Delete testing automatically occurs in TestCase
+ },
+ })
+}
+
+func testAccGitLabResourceConfig(spaceID, intName string, discoveryGroup bool) string {
+ return fmt.Sprintf(`
+resource "mondoo_integration_gitlab" "test" {
+ space_id = %[1]q
+ name = %[2]q
+ base_url = "https://my-self-hosted-gitlab.com"
+ group = "my-group"
+ discovery = {
+ groups = %[3]t
+ projects = true
+ terraform = true
+ k8s_manifests = true
+ }
+ credentials = {
+ token = "abcd1234567890"
+ }
+}
+`, spaceID, intName, discoveryGroup)
+}
+
+func testAccGitLabResourceWithSpaceInProviderConfig(spaceID, intName, token string) string {
+ return fmt.Sprintf(`
+provider "mondoo" {
+ space = %[1]q
+}
+resource "mondoo_integration_gitlab" "test" {
+ name = %[2]q
+ base_url = "https://my-self-hosted-gitlab.com"
+ group = "my-group"
+ discovery = {
+ groups = true
+ projects = true
+ terraform = true
+ k8s_manifests = true
+ }
+ credentials = {
+ token = %[3]q
+ }
+}
+`, spaceID, intName, token)
+}
diff --git a/internal/provider/integration_jira_resource.go b/internal/provider/integration_jira_resource.go
new file mode 100644
index 0000000..6455a79
--- /dev/null
+++ b/internal/provider/integration_jira_resource.go
@@ -0,0 +1,295 @@
+package provider
+
+import (
+ "context"
+ "fmt"
+ "regexp"
+
+ "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
+ "github.com/hashicorp/terraform-plugin-framework/resource"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/schema/validator"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/hashicorp/terraform-plugin-log/tflog"
+ mondoov1 "go.mondoo.com/mondoo-go"
+)
+
+var _ resource.Resource = (*integrationJiraResource)(nil)
+
+func NewIntegrationJiraResource() resource.Resource {
+ return &integrationJiraResource{}
+}
+
+type integrationJiraResource struct {
+ client *ExtendedGqlClient
+}
+
+type integrationJiraResourceModel struct {
+ SpaceID types.String `tfsdk:"space_id"`
+
+ // integration details
+ Mrn types.String `tfsdk:"mrn"`
+ Name types.String `tfsdk:"name"`
+ Host types.String `tfsdk:"host"`
+ Email types.String `tfsdk:"email"`
+
+ // Optional settings
+ DefaultProject types.String `tfsdk:"default_project"`
+ AutoCreate types.Bool `tfsdk:"auto_create"`
+ AutoClose types.Bool `tfsdk:"auto_close"`
+
+ // credentials
+ Credential *integrationJiraCredentialModel `tfsdk:"credentials"`
+}
+
+type integrationJiraCredentialModel struct {
+ Token types.String `tfsdk:"token"`
+}
+
+func (r *integrationJiraResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
+ resp.TypeName = req.ProviderTypeName + "_integration_jira"
+}
+
+func (m integrationJiraResourceModel) GetConfigurationOptions() *mondoov1.JiraConfigurationOptionsInput {
+ opts := &mondoov1.JiraConfigurationOptionsInput{
+ Host: mondoov1.String(m.Host.ValueString()),
+ Email: mondoov1.String(m.Email.ValueString()),
+ APIToken: mondoov1.String(m.Credential.Token.ValueString()),
+ DefaultProject: mondoov1.String(m.DefaultProject.ValueString()),
+ AutoCreateCases: mondoov1.NewBooleanPtr(mondoov1.Boolean(m.AutoCreate.ValueBool())),
+ AutoCloseTickets: mondoov1.NewBooleanPtr(mondoov1.Boolean(m.AutoClose.ValueBool())),
+ }
+
+ return opts
+}
+
+func (r *integrationJiraResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
+ resp.Schema = schema.Schema{
+ MarkdownDescription: `Integrate the Ticketing System Jira with Mondoo to automatically create and close issues based on Mondoo findings.`,
+ Attributes: map[string]schema.Attribute{
+ "space_id": schema.StringAttribute{
+ MarkdownDescription: "Mondoo Space Identifier. If it is not provided, the provider space is used.",
+ Optional: true,
+ Computed: true,
+ PlanModifiers: []planmodifier.String{
+ stringplanmodifier.UseStateForUnknown(),
+ },
+ },
+ "mrn": schema.StringAttribute{
+ Computed: true,
+ MarkdownDescription: "Integration identifier.",
+ PlanModifiers: []planmodifier.String{
+ stringplanmodifier.UseStateForUnknown(),
+ },
+ },
+ "name": schema.StringAttribute{
+ MarkdownDescription: "Name of the integration.",
+ Required: true,
+ Validators: []validator.String{
+ stringvalidator.LengthAtMost(250),
+ },
+ },
+ "host": schema.StringAttribute{
+ MarkdownDescription: "Jira host URL.",
+ Required: true,
+ Validators: []validator.String{
+ stringvalidator.RegexMatches(
+ regexp.MustCompile(`^https?:\/\/[a-zA-Z0-9\-._~:\/?#[\]@!$&'()*+,;=%]+$`),
+ "must be a valid URL",
+ ),
+ },
+ },
+ "email": schema.StringAttribute{
+ MarkdownDescription: "Jira user email.",
+ Required: true,
+ Validators: []validator.String{
+ stringvalidator.RegexMatches(
+ regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`),
+ "must be a valid email",
+ ),
+ },
+ },
+ "default_project": schema.StringAttribute{
+ MarkdownDescription: "Default Jira project (is represented by the project key e.g. `MONDOO`).",
+ Optional: true,
+ },
+ "auto_create": schema.BoolAttribute{
+ MarkdownDescription: "Automatically create Jira issues for Mondoo findings.",
+ Optional: true,
+ },
+ "auto_close": schema.BoolAttribute{
+ MarkdownDescription: "Automatically close Jira issues for resolved Mondoo findings",
+ Optional: true,
+ },
+ "credentials": schema.SingleNestedAttribute{
+ Required: true,
+ Attributes: map[string]schema.Attribute{
+ "token": schema.StringAttribute{
+ MarkdownDescription: "Jira API token.",
+ Required: true,
+ Sensitive: true,
+ },
+ },
+ },
+ },
+ }
+}
+
+func (r *integrationJiraResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
+ // Prevent panic if the provider has not been configured.
+ if req.ProviderData == nil {
+ return
+ }
+
+ client, ok := req.ProviderData.(*ExtendedGqlClient)
+
+ if !ok {
+ resp.Diagnostics.AddError(
+ "Unexpected Resource Configure Type",
+ fmt.Sprintf("Expected *http.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
+ )
+
+ return
+ }
+
+ r.client = client
+}
+
+func (r *integrationJiraResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
+ var data integrationJiraResourceModel
+
+ // Read Terraform plan data into the model
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
+
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ // Compute and validate the space
+ space, err := r.client.ComputeSpace(data.SpaceID)
+ if err != nil {
+ resp.Diagnostics.AddError("Invalid Configuration", err.Error())
+ return
+ }
+ ctx = tflog.SetField(ctx, "space_mrn", space.MRN())
+
+ // Do GraphQL request to API to create the resource.
+ tflog.Debug(ctx, "Creating integration")
+ integration, err := r.client.CreateIntegration(ctx,
+ space.MRN(),
+ data.Name.ValueString(),
+ mondoov1.ClientIntegrationTypeTicketSystemJira,
+ mondoov1.ClientIntegrationConfigurationInput{
+ JiraConfigurationOptions: data.GetConfigurationOptions(),
+ })
+ if err != nil {
+ resp.Diagnostics.
+ AddError("Client Error",
+ fmt.Sprintf("Unable to create Jira integration, got error: %s", err),
+ )
+ return
+ }
+
+ // Save space mrn into the Terraform state.
+ data.Mrn = types.StringValue(string(integration.Mrn))
+ data.Name = types.StringValue(string(integration.Name))
+ data.SpaceID = types.StringValue(space.ID())
+
+ // Save data into Terraform state
+ resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
+
+func (r *integrationJiraResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
+ var data integrationJiraResourceModel
+
+ // Read Terraform prior state data into the model
+ resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
+
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ // Read API call logic
+
+ // Save updated data into Terraform state
+ resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
+
+func (r *integrationJiraResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
+ var data integrationJiraResourceModel
+
+ // Read Terraform plan data into the model
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
+
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ // Do GraphQL request to API to update the resource.
+ opts := mondoov1.ClientIntegrationConfigurationInput{
+ JiraConfigurationOptions: data.GetConfigurationOptions(),
+ }
+
+ _, err := r.client.UpdateIntegration(ctx,
+ data.Mrn.ValueString(),
+ data.Name.ValueString(),
+ mondoov1.ClientIntegrationTypeTicketSystemJira,
+ opts,
+ )
+ if err != nil {
+ resp.Diagnostics.
+ AddError("Client Error",
+ fmt.Sprintf("Unable to update Jira integration, got error: %s", err),
+ )
+ return
+ }
+
+ // Save updated data into Terraform state
+ resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
+
+func (r *integrationJiraResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
+ var data integrationJiraResourceModel
+
+ // Read Terraform prior state data into the model
+ resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
+
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ // Do GraphQL request to API to update the resource.
+ _, err := r.client.DeleteIntegration(ctx, data.Mrn.ValueString())
+ if err != nil {
+ resp.Diagnostics.
+ AddError("Client Error",
+ fmt.Sprintf("Unable to delete Jira integration, got error: %s", err),
+ )
+ return
+ }
+}
+
+func (r *integrationJiraResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
+ integration, ok := r.client.ImportIntegration(ctx, req, resp)
+ if !ok {
+ return
+ }
+
+ model := integrationJiraResourceModel{
+ Mrn: types.StringValue(integration.Mrn),
+ Name: types.StringValue(integration.Name),
+ SpaceID: types.StringValue(integration.SpaceID()),
+ Host: types.StringValue(integration.ConfigurationOptions.JiraConfigurationOptions.Host),
+ Email: types.StringValue(integration.ConfigurationOptions.JiraConfigurationOptions.Email),
+ DefaultProject: types.StringValue(integration.ConfigurationOptions.JiraConfigurationOptions.DefaultProject),
+ AutoCreate: types.BoolValue(integration.ConfigurationOptions.JiraConfigurationOptions.AutoCreateCases),
+ AutoClose: types.BoolValue(integration.ConfigurationOptions.JiraConfigurationOptions.AutoCloseTickets),
+ Credential: &integrationJiraCredentialModel{
+ Token: types.StringPointerValue(nil),
+ },
+ }
+
+ resp.State.Set(ctx, &model)
+}
diff --git a/internal/provider/integration_jira_resource_test.go b/internal/provider/integration_jira_resource_test.go
new file mode 100644
index 0000000..c30cb3c
--- /dev/null
+++ b/internal/provider/integration_jira_resource_test.go
@@ -0,0 +1,103 @@
+// Copyright (c) Mondoo, Inc.
+// SPDX-License-Identifier: BUSL-1.1
+
+package provider
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+)
+
+func TestAccJiraResource(t *testing.T) {
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
+ Steps: []resource.TestStep{
+ // Create and Read testing
+ {
+ Config: testAccJiraResourceConfig(accSpace.ID(), "one", "https://your-instance.atlassian.net", "jira.owner@email.com", "MONDOO"),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("mondoo_integration_jira.test", "name", "one"),
+ resource.TestCheckResourceAttr("mondoo_integration_jira.test", "space_id", accSpace.ID()),
+ resource.TestCheckResourceAttr("mondoo_integration_jira.test", "host", "https://your-instance.atlassian.net"),
+ resource.TestCheckResourceAttr("mondoo_integration_jira.test", "email", "jira.owner@email.com"),
+ resource.TestCheckResourceAttr("mondoo_integration_jira.test", "default_project", "MONDOO"),
+ ),
+ },
+ {
+ Config: testAccJiraResourceWithSpaceInProviderConfig(accSpace.ID(), "two", "abctoken12345", true, false),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("mondoo_integration_jira.test", "name", "two"),
+ resource.TestCheckResourceAttr("mondoo_integration_jira.test", "space_id", accSpace.ID()),
+ resource.TestCheckResourceAttr("mondoo_integration_jira.test", "credentials.token", "abctoken12345"),
+ resource.TestCheckResourceAttr("mondoo_integration_jira.test", "auto_create", "true"),
+ resource.TestCheckResourceAttr("mondoo_integration_jira.test", "auto_close", "false"),
+ ),
+ },
+ // Update and Read testing
+ {
+ Config: testAccJiraResourceConfig(accSpace.ID(), "one", "https://your-instance.atlassian.net", "jira.owner@email.com", "MONDOO"),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("mondoo_integration_jira.test", "name", "one"),
+ resource.TestCheckResourceAttr("mondoo_integration_jira.test", "space_id", accSpace.ID()),
+ resource.TestCheckResourceAttr("mondoo_integration_jira.test", "host", "https://your-instance.atlassian.net"),
+ resource.TestCheckResourceAttr("mondoo_integration_jira.test", "email", "jira.owner@email.com"),
+ resource.TestCheckResourceAttr("mondoo_integration_jira.test", "default_project", "MONDOO"),
+ ),
+ },
+ {
+ Config: testAccJiraResourceWithSpaceInProviderConfig(accSpace.ID(), "two", "abctoken12345", false, true),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttr("mondoo_integration_jira.test", "name", "two"),
+ resource.TestCheckResourceAttr("mondoo_integration_jira.test", "space_id", accSpace.ID()),
+ resource.TestCheckResourceAttr("mondoo_integration_jira.test", "credentials.token", "abctoken12345"),
+ resource.TestCheckResourceAttr("mondoo_integration_jira.test", "auto_create", "false"),
+ resource.TestCheckResourceAttr("mondoo_integration_jira.test", "auto_close", "true"),
+ ),
+ },
+ // Delete testing automatically occurs in TestCase
+ },
+ })
+}
+
+func testAccJiraResourceConfig(spaceID, intName, host, email, defaultProject string) string {
+ return fmt.Sprintf(`
+resource "mondoo_integration_jira" "test" {
+ space_id = %[1]q
+ name = %[2]q
+ host = %[3]q
+ email = %[4]q
+ default_project = %[5]q
+
+ auto_create = true
+ auto_close = true
+
+ credentials = {
+ token = "abcd1234567890"
+ }
+}
+`, spaceID, intName, host, email, defaultProject)
+}
+
+func testAccJiraResourceWithSpaceInProviderConfig(spaceID, intName, token string, autoCreate, autoClose bool) string {
+ return fmt.Sprintf(`
+provider "mondoo" {
+ space = %[1]q
+}
+resource "mondoo_integration_jira" "test" {
+ name = %[2]q
+ host = "https://your-instance.atlassian.net"
+ email = "jira.owner@email.com"
+ default_project = "MONDOO"
+
+ auto_create = %[4]t
+ auto_close = %[5]t
+
+ credentials = {
+ token = %[3]q
+ }
+}
+`, spaceID, intName, token, autoCreate, autoClose)
+}
diff --git a/internal/provider/provider.go b/internal/provider/provider.go
index 059bc97..91422f1 100644
--- a/internal/provider/provider.go
+++ b/internal/provider/provider.go
@@ -205,6 +205,8 @@ func (p *MondooProvider) Resources(ctx context.Context) []func() resource.Resour
NewFrameworkAssignmentResource,
NewCustomFrameworkResource,
NewExceptionResource,
+ NewIntegrationJiraResource,
+ NewIntegrationEmailResource,
NewIntegrationGitlabResource,
}
}