auth0/resource_auth0_action.go
package auth0
import (
"fmt"
"net/http"
"strings"
"github.com/alexkappa/terraform-provider-auth0/auth0/internal/hash"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
"gopkg.in/auth0.v5/management"
)
func newAction() *schema.Resource {
return &schema.Resource{
Create: createAction,
Read: readAction,
Update: updateAction,
Delete: deleteAction,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
Description: "The name of an action",
},
"supported_triggers": {
Type: schema.TypeList,
Required: true,
MinItems: 1,
MaxItems: 1, // NOTE: Changes must be made together with expandAction()
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"id": {
Type: schema.TypeString,
Required: true,
Description: "Trigger ID",
},
"version": {
Type: schema.TypeString,
Required: true,
Description: "Trigger version",
},
},
},
Description: "List of triggers that this action supports. At " +
"this time, an action can only target a single trigger at" +
" a time",
},
"code": {
Type: schema.TypeString,
Required: true,
Description: "The source code of the action.",
},
"dependencies": {
Type: schema.TypeSet,
Optional: true,
MinItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
Description: "Dependency name. For example lodash",
},
"version": {
Type: schema.TypeString,
Required: true,
Description: "Dependency version. For example `latest` or `4.17.21`",
},
},
},
Set: hash.StringKey("name"),
Description: "List of third party npm modules, and their versions, that this action depends on",
},
"runtime": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: validation.StringInSlice([]string{
"node12",
"node16",
}, false),
Description: "The Node runtime. For example `node16`, defaults to `node12`",
},
"secrets": {
Type: schema.TypeList,
Optional: true,
MinItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
Description: "Secret name",
},
"value": {
Type: schema.TypeString,
Required: true,
Sensitive: true,
Description: "Secret value",
},
},
},
Description: "List of secrets that are included in an action or a version of an action",
},
"deploy": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "Deploying an action will create a new immutable" +
" version of the action. If the action is currently bound" +
" to a trigger, then the system will begin executing the " +
"newly deployed version of the action immediately",
},
"version_id": {
Type: schema.TypeString,
Computed: true,
Description: "Version ID of the action. This value is available if `deploy` is set to true",
},
},
}
}
func createAction(d *schema.ResourceData, m interface{}) error {
api := m.(*management.Management)
a := expandAction(d)
err := api.Action.Create(a)
if err != nil {
return err
}
d.SetId(a.GetID())
d.Partial(true)
err = deployAction(d, m)
if err != nil {
return err
}
d.Partial(false)
return readAction(d, m)
}
func readAction(d *schema.ResourceData, m interface{}) error {
api := m.(*management.Management)
a, err := api.Action.Read(d.Id())
if err != nil {
if mErr, ok := err.(management.Error); ok {
if mErr.Status() == http.StatusNotFound {
d.SetId("")
return nil
}
}
return err
}
d.Set("name", a.Name)
d.Set("supported_triggers", flattenActionTriggers(a.SupportedTriggers))
d.Set("code", a.Code)
d.Set("dependencies", flattenActionDependencies(a.Dependencies))
d.Set("runtime", a.Runtime)
if a.DeployedVersion != nil {
d.Set("version_id", a.DeployedVersion.GetID())
}
return nil
}
func updateAction(d *schema.ResourceData, m interface{}) error {
a := expandAction(d)
api := m.(*management.Management)
err := api.Action.Update(d.Id(), a)
if err != nil {
return err
}
d.Partial(true)
err = deployAction(d, m)
if err != nil {
return err
}
d.Partial(false)
return readAction(d, m)
}
func deployAction(d *schema.ResourceData, m interface{}) error {
if d.Get("deploy").(bool) == true {
api := m.(*management.Management)
err := resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError {
a, err := api.Action.Read(d.Id())
if err != nil {
return resource.NonRetryableError(err)
}
if strings.ToLower(a.GetStatus()) != "built" {
return resource.RetryableError(
fmt.Errorf(`Expected action status %q to equal "built"`, a.GetStatus()),
)
}
return nil
})
if err != nil {
return fmt.Errorf("Action never reached built state. %w", err)
}
v, err := api.Action.Deploy(d.Id())
if err != nil {
return err
}
d.Set("version_id", v.GetID())
}
return nil
}
func deleteAction(d *schema.ResourceData, m interface{}) error {
api := m.(*management.Management)
if err := api.Action.Delete(d.Id()); err != nil {
if mErr, ok := err.(management.Error); ok {
if mErr.Status() == http.StatusNotFound {
d.SetId("")
return nil
}
}
return err
}
return nil
}
func expandAction(d *schema.ResourceData) *management.Action {
a := &management.Action{
Name: String(d, "name"),
Code: String(d, "code"),
Runtime: String(d, "runtime"),
}
List(d, "supported_triggers").Elem(func(d ResourceData) {
a.SupportedTriggers = []*management.ActionTrigger{
{
ID: String(d, "id"),
Version: String(d, "version"),
},
}
})
Set(d, "dependencies").Elem(func(d ResourceData) {
a.Dependencies = append(a.Dependencies, &management.ActionDependency{
Name: String(d, "name"),
Version: String(d, "version"),
})
})
List(d, "secrets").Elem(func(d ResourceData) {
a.Secrets = append(a.Secrets, &management.ActionSecret{
Name: String(d, "name"),
Value: String(d, "value"),
})
})
return a
}
func flattenActionTriggers(triggers []*management.ActionTrigger) (ret []interface{}) {
for _, trigger := range triggers {
ret = append(ret, map[string]interface{}{
"id": trigger.ID,
"version": trigger.Version,
})
}
return
}
func flattenActionDependencies(dependencies []*management.ActionDependency) (ret []interface{}) {
for _, dependency := range dependencies {
ret = append(ret, map[string]interface{}{
"name": dependency.Name,
"version": dependency.Version,
})
}
return
}
func flattenActionSecrets(secrets []*management.ActionSecret) (ret []interface{}) {
for _, secret := range secrets {
ret = append(ret, map[string]interface{}{
"name": secret.Name,
"value": secret.Value,
})
}
return
}