diff --git a/README.md b/README.md index 914ee35..5886c4e 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ A very nascent Terraform provider for configuring [ImprovMX](https://improvmx.co - Delete an email forward. - Import an email forward. - Update an email forward (ImprovMX allows updating an email forward to send to more than one address, ie `alice@example.com,bob@example.com`). +- Create, update and delete a domain's SMTP credentials. ## Usage @@ -19,7 +20,7 @@ A very nascent Terraform provider for configuring [ImprovMX](https://improvmx.co terraform { required_providers { improvmx = { - source = "issyl0/improvmx" + source = "issyl0/improvmx" } } } diff --git a/docs/resources/improvmx_smtp_credential.md b/docs/resources/improvmx_smtp_credential.md new file mode 100644 index 0000000..1a1c882 --- /dev/null +++ b/docs/resources/improvmx_smtp_credential.md @@ -0,0 +1,19 @@ +# `improvmx_smtp_credential` Resource + +A resource to create ImprovMX domain SMTP credentials. + +## Example Usage + +```hcl +resource "improvmx_email_forward" "example" { + domain = "example.com" + username = "example" + password = var.password +} +``` + +## Argument Reference + +* `domain` - (Required) Name of the domain. +* `username` - (Required) Username of the SMTP sender. +* `password` - (Required) Password for the SMTP sending account. diff --git a/improvmx/provider.go b/improvmx/provider.go index c857255..cfcb4e4 100644 --- a/improvmx/provider.go +++ b/improvmx/provider.go @@ -15,8 +15,12 @@ type Meta struct { func Provider() *schema.Provider { p := &schema.Provider{ - Schema: map[string]*schema.Schema{"token": {Type: schema.TypeString, Required: true, DefaultFunc: schema.EnvDefaultFunc("IMPROVMX_API_TOKEN", nil), Description: "The API token for API operations."}}, - ResourcesMap: map[string]*schema.Resource{"improvmx_domain": resourceDomain(), "improvmx_email_forward": resourceEmailForward()}, + Schema: map[string]*schema.Schema{"token": {Type: schema.TypeString, Required: true, DefaultFunc: schema.EnvDefaultFunc("IMPROVMX_API_TOKEN", nil), Description: "The API token for API operations."}}, + ResourcesMap: map[string]*schema.Resource{ + "improvmx_domain": resourceDomain(), + "improvmx_email_forward": resourceEmailForward(), + "improvmx_smtp_credential": resourceSMTPCredential(), + }, DataSourcesMap: map[string]*schema.Resource{}, ProviderMetaSchema: map[string]*schema.Schema{}, } diff --git a/improvmx/resource_smtp_credential.go b/improvmx/resource_smtp_credential.go new file mode 100644 index 0000000..d0061f8 --- /dev/null +++ b/improvmx/resource_smtp_credential.go @@ -0,0 +1,120 @@ +package improvmx + +import ( + "fmt" + "log" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceSMTPCredential() *schema.Resource { + return &schema.Resource{ + Create: resourceSMTPCredentialCreate, + Read: resourceSMTPCredentialRead, + Update: resourceSMTPCredentialUpdate, + Delete: resourceSMTPCredentialDelete, + + Schema: map[string]*schema.Schema{ + "domain": { + Type: schema.TypeString, + Required: true, + }, + "username": { + Type: schema.TypeString, + Required: true, + }, + "password": { + Type: schema.TypeString, + Required: true, + Sensitive: true, + }, + }, + } +} + +func resourceSMTPCredentialCreate(d *schema.ResourceData, meta interface{}) error { + m := meta.(*Meta) + + for { + resp := m.Client.CreateSMTPCredential(d.Get("domain").(string), d.Get("username").(string), d.Get("password").(string)) + + log.Printf("[DEBUG] Got status code %v from ImprovMX API on Create for SMTP %s@%s, success: %v, errors: %v.", resp.Code, d.Get("username").(string), d.Get("domain").(string), resp.Success, resp.Errors) + + if resp.Code == 429 { + log.Printf("[DEBUG] Sleeping for 10 seconds to allow rate limit to recover.") + time.Sleep(10 * time.Second) + } + + if resp.Code == 404 { + log.Printf("[DEBUG] Couldn't find the resource in ImprovMX. Aborting") + return fmt.Errorf("HTTP response code %d, error text: %s", resp.Code, resp.Errors.Domain) + } + + if resp.Success { + return resourceSMTPCredentialRead(d, meta) + } + } +} + +func resourceSMTPCredentialRead(d *schema.ResourceData, meta interface{}) error { + m := meta.(*Meta) + m.Mutex.Lock() + defer m.Mutex.Unlock() + + for { + resp := m.Client.GetSMTPCredential(d.Get("domain").(string)) + + log.Printf("[DEBUG] Got status code %v from ImprovMX API on Read for SMTP domain %s, success: %v, errors: %v.", resp.Code, d.Get("domain").(string), resp.Success, resp.Errors) + + if resp.Code == 429 { + log.Printf("[DEBUG] Sleeping for 10 seconds to allow rate limit to recover.") + time.Sleep(10 * time.Second) + } + + if resp.Code == 404 { + log.Printf("[DEBUG] Couldn't find the resource in ImprovMX. Aborting") + return fmt.Errorf("HTTP response code %d, error text: %s", resp.Code, resp.Errors.Domain) + } + + if resp.Success { + d.SetId(d.Get("domain").(string)) + d.Set("domain", d.Get("domain").(string)) + return nil + } + } +} + +func resourceSMTPCredentialUpdate(d *schema.ResourceData, meta interface{}) error { + m := meta.(*Meta) + + for { + resp := m.Client.UpdateSMTPCredential(d.Get("domain").(string), d.Get("username").(string), d.Get("password").(string)) + + log.Printf("[DEBUG] Got status code %v from ImprovMX API on Update for SMTP domain %s@%s, success: %v, errors: %v.", resp.Code, d.Get("username").(string), d.Get("domain").(string), resp.Success, resp.Errors) + + if resp.Code == 429 { + log.Printf("[DEBUG] Sleeping for 10 seconds to allow rate limit to recover.") + time.Sleep(10 * time.Second) + } + + if resp.Code == 404 { + log.Printf("[DEBUG] Couldn't find the resource in ImprovMX. Aborting") + return fmt.Errorf("HTTP response code %d, error text: %s", resp.Code, resp.Errors.Domain) + } + + if resp.Success { + return resourceSMTPCredentialRead(d, meta) + } + } +} + +func resourceSMTPCredentialDelete(d *schema.ResourceData, meta interface{}) error { + m := meta.(*Meta) + m.Mutex.Lock() + defer m.Mutex.Unlock() + + m.Client.DeleteSMTPCredential(d.Get("domain").(string), d.Get("username").(string)) + + return nil +}