Is it possible to require exclusive or for resource properties in a terraform provider? And, how?

Viewed 53

It's simple: I want to be able to support directly adding content to the TF script, or refer to a file with that data, but not both. When I try this, I get an error

content: ConflictsWith cannot be set with Required

Schema: map[string]*schema.Schema{
            "content": {
                Type:      schema.TypeString,
                Required:  true,
                Sensitive: true,
                ConflictsWith: []string{"file"},
            },
            "file": {
                Type: schema.TypeString,
                Sensitive: true,
                Required: true,
                ConflictsWith: []string{"content"},
            },
     }

How can I make this terraform provider either accept a file, OR content, but not both? Obviously, if I set both to optional then neither is required.

1 Answers

Actually, this was easier than I thought. Looking at the schema.Schema struct produced an answer:

ExactlyOneOf

        schema.Resource{
            Schema: map[string]*schema.Schema{
                "alias": {
                    Type:     schema.TypeString,
                    Required: true,
                    ForceNew: true,
                },
                "content": {
                    Type:          schema.TypeString,
                    Optional:      true,
                    Sensitive:     true,
                },
                "file": {
                    Type:          schema.TypeString,
                    Sensitive:     true,
                    Optional:      true,
                    ExactlyOneOf: []string{"content", "file"},
                },
            }
        }

Which, when tested, produces this error: - "file": only one of content,filecan be specified, butcontent,file were specified.

We can thus use the ExpectError and match this

func TestHasFileAndContentFails(t *testing.T){
    const conflictsResource = `
        resource "artifactory_certificate" "fail" {
            alias   = "fail"
            file = "/this/doesnt/exist.pem"
            content = "PEM DATA"
        }
    `
    resource.Test(t, resource.TestCase{
        Providers:    testAccProviders,
        Steps: []resource.TestStep{
            {
                Config:      conflictsResource,
                ExpectError: regexp.MustCompile(`.*only one of .* can be specified, but .* were specified.*`),
            },
        },
    })
}

It's brittle in that a debug string now has meaning, and I would expect the validation to be on the entire schema as a final phase. But, this works

Related