这是indexloc提供的服务,不要输入任何密码
Skip to content

ProtectionBypassForAutomationSecret Update only happens when ProtectionBypassForAutomation is toggled #294

@DavidS-ovm

Description

@DavidS-ovm

When setting protection_bypass_for_automation and protection_bypass_for_automation_secret through vercel_project, the value is correctly updated in the terraform state, but not in the actual project as confirmed through the UI.

code:

# Vercel Protection Bypass for Automation requires the secret to be exactly 32
# characters long, and only if we inject the value can we also use it elsewhere,
# e.g. to put it into 1password for Github Actions.
resource "random_password" "vercel_automation_bypass" {
  count   = var.terraform_env_name == "dogfood" ? 1 : 0
  length  = 32
  special = false # vercel does not like special characters
}

# Configure Protection Bypass for Automation on the project. This is only done
# in dogfood, as we primarily use vercel for code review deployments on PRs.
resource "vercel_project" "frontend" {
  count   = var.terraform_env_name == "dogfood" ? 1 : 0
  name    = "frontend"
  team_id = var.vercel_team_id

  enable_affected_projects_deployments = true
  framework                            = "nextjs"
  git_repository = {
    type              = "github"
    repo              = "overmindtech/workspace"
    production_branch = "main"
  }
  protection_bypass_for_automation        = true
  protection_bypass_for_automation_secret = random_password.vercel_automation_bypass[0].result
  root_directory                          = "frontend"
  skew_protection                         = "12 hours"
}

resource "onepassword_item" "vercel_generated" {
  count = var.terraform_env_name == "dogfood" ? 1 : 0
  vault = data.onepassword_vault.global_vault.uuid

  title      = "vercel"
  category   = "secure_note"
  note_value = "generated items from vercel; provisioned through the dogfood terraform environment"

  section {
    label = "generated"
    field {
      label = "info: VERCEL_AUTOMATION_BYPASS_SECRET"
      type  = "STRING"
      value = "https://vercel.com/overmindtech/frontend/settings/deployment-protection; this is provisioned from terraform"
    }
    field {
      label = "VERCEL_AUTOMATION_BYPASS_SECRET"
      type  = "CONCEALED"
      value = random_password.vercel_automation_bypass[0].result
    }
  }
}

plan:

[...]
vercel_project.frontend[0]: Refreshing state... [id=prj_pobSoGaGRB9xUIAp3ckLPNRvWMsM]
[...]
  # random_password.vercel_automation_bypass[0] is tainted, so must be replaced
-/+ resource "random_password" "vercel_automation_bypass" {
      ~ bcrypt_hash = (sensitive value)
      ~ id          = "none" -> (known after apply)
      ~ result      = (sensitive value)
        # (10 unchanged attributes hidden)
    }
[...]
  # vercel_project.frontend[0] will be updated in-place
  ~ resource "vercel_project" "frontend" {
        id                                                = "prj_pobSoGaGRB9xUIAp3ckLPNRvWMsM"
        name                                              = "frontend"
      ~ protection_bypass_for_automation_secret           = (sensitive value)
        # (20 unchanged attributes hidden)
    }
[...]
  # onepassword_item.vercel_generated[0] will be updated in-place
  ~ resource "onepassword_item" "vercel_generated" {
        id         = "vaults/smyzrf7ekx2hlq7oq6kntecca4/items/qektdrtygrybtjecadu7777hjy"
      + password   = (sensitive value)
        # (5 unchanged attributes hidden)

      ~ section {
            id    = "74443c7f-eb6b-4ecf-83ab-52e2c279eb83"
            # (1 unchanged attribute hidden)

          ~ field {
                id    = "xwjdroeux4bqkaz2hgnta5vaqu"
              ~ value = (sensitive value)
                # (2 unchanged attributes hidden)
            }

            # (1 unchanged block hidden)
        }
    }

apply:

random_password.vercel_automation_bypass[0]: Destroying... [id=none]
random_password.vercel_automation_bypass[0]: Destruction complete after 0s
random_password.vercel_automation_bypass[0]: Creating...
random_password.vercel_automation_bypass[0]: Creation complete after 0s [id=none]
onepassword_item.vercel_generated[0]: Modifying... [id=vaults/smyzrf7ekx2hlq7oq6kntecca4/items/qektdrtygrybtjecadu7777hjy]
vercel_project.frontend[0]: Modifying... [id=prj_pobSoGaGRB9xUIAp3ckLPNRvWMsM]
onepassword_item.vercel_generated[0]: Modifications complete after 1s [id=vaults/smyzrf7ekx2hlq7oq6kntecca4/items/qektdrtygrybtjecadu7777hjy]
vercel_project.frontend[0]: Modifications complete after 1s [id=prj_pobSoGaGRB9xUIAp3ckLPNRvWMsM]
[...]

Apply complete! Resources: 26 added, 8 changed, 2 destroyed.

Checking the new state:

terraform plan -out=tfplan
terraform show -json tfplan 2>&1 | jq | less

Searching for random_password.vercel_automation_bypass and vercel_project.frontend in the JSON output shows the same value as stored in 1password (proving that the value has been correctly applied outside) on both random_password.vercel_automation_bypass.result (the generated value) as well as on vercel_project.frontend[0].protection_bypass_for_automation_secret (proving that the value has been correctly passed to the vercel_project provider).

Reading through the client code at

func getUpdateBypassProtectionRequestBody(newValue bool, secret string) string {
if newValue {
if secret == "" {
return "{}"
}
return string(mustMarshal(struct {
Revoke generateBypassProtectionRequest `json:"generate"`
}{
Revoke: generateBypassProtectionRequest{
Secret: secret,
},
}))
}
return string(mustMarshal(struct {
Revoke revokeBypassProtectionRequest `json:"revoke"`
}{
Revoke: revokeBypassProtectionRequest{
Regenerate: false,
Secret: secret,
},
}))
}
I'm getting the feeling that this should read

func getUpdateBypassProtectionRequestBody(newValue bool, secret string) string {
	if newValue {
		if secret == "" {
			return "{}"
		}
		return string(mustMarshal(struct {
			Generate generateBypassProtectionRequest `json:"generate"`
		}{
			Generate: generateBypassProtectionRequest{
				Secret: secret,
			},
		}))
	}


	return string(mustMarshal(struct {
		Revoke revokeBypassProtectionRequest `json:"revoke"`
	}{
		Revoke: revokeBypassProtectionRequest{
			Regenerate: false,
			Secret:     secret,
		},
	}))
}

instead, but that's just a red herring that had me chasing my tail for 10 minutes.

The true error is that

if state.ProtectionBypassForAutomation != plan.ProtectionBypassForAutomation {
is only checking whether the ProtectionBypassForAutomation flag has been toggled, but not if the secret is still the same.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions