terraform-provider-aws: aws_codepipeline with Github OAuth causing persistent changes

Terraform Version

Terraform v0.11.1 Terraform AWS Provider v1.6.0

Affected Resource(s)

Please list the resources as a list, for example:

  • aws_codepipeline

Terraform Configuration Files

resource "aws_codepipeline" "build" {
  name     = "pipeline-test"
  role_arn = "pipeline-test"

  artifact_store {
    type     = "S3"
    location = "pipeline-test-bucket"
  }

  stage {
    name = "Source"

    action {
      name     = "Source"
      category = "Source"
      owner    = "ThirdParty"
      provider = "GitHub"
      version  = "1"

      output_artifacts = ["code"]

      configuration {
        OAuthToken           = "${var.github_token}"
        Owner                = "${var.github_owner}"
        Repo                 = "${var.github_repo}"
        Branch               = "${var.github_branch}"
        PollForSourceChanges = "true"
      }
    }
  }

  stage {
    name = "Build"

    action {
      name     = "Build"
      category = "Build"
      owner    = "AWS"
      provider = "CodeBuild"
      version  = "1"

      input_artifacts  = ["code"]
      output_artifacts = ["package"]

      configuration {
        ProjectName = "${var.project_name}"
      }
    }
  }
}

Expected Behavior

Subsequent executions of terraform apply should not result in updates to the source attributes.

Actual Behavior

Running terraform plan/terraform apply always results in a change:

  ~ aws_codepipeline.build
      stage.0.action.0.configuration.%:          "4" => "5"
      stage.0.action.0.configuration.OAuthToken: "" => "REDACTED"

And AWS is incapable of accessing Github, even though the token is valid, tested, and with the correct scopes.

Steps to Reproduce

Please list the steps required to reproduce the issue, for example:

  1. terraform plan
  2. terraform apply
  3. terraform apply

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 52
  • Comments: 21 (5 by maintainers)

Most upvoted comments

I had a look at the provider code and it seems that the OAuthToken is getting deleted from the state file.

if ok && actionProvider == "GitHub" {
				delete(config, "OAuthToken")
			}

I suspect this has been done to not store secrets in state file. However, in other resources like aws_db_instance, we store the passwords in state file. The state file always has been the single source of truth. The issue pointed out here violates that principal and kind of degrades the developer experience.

I suggest we change this behaviour and store the token in the state file and keep the experience consistent across resource.

Moreover, the OAuthToken value is taken from an environment variable, which is again not consistent with other resources.

I’m experiencing the same issue, but managed to work around it by adding the following to my aws_codepipeline resource:

  # Workaround for Terraform insisting on "updating" OAuthToken every run. In
  # the event that the OAuthToken actually needs to be updated, comment out
  # the `lifecycle` block, run `terraform apply`, and then restore the block.
  #
  # https://github.com/terraform-providers/terraform-provider-aws/issues/2854
  lifecycle {
    ignore_changes = [
      "stage.0.action.0.configuration.OAuthToken",
      "stage.0.action.0.configuration.%",
    ]
  }

The GitHub token isn’t likely to change often in my use case, so the inconvenience of having to remove & restore that lifecycle block is not a big deal compared to having to confirm that I want to “change” the token on every single run (and having it displayed on the screen in plaintext each time, too).

It doesn’t address the root cause, but hopefully someone else will find this workaround useful.

EDIT (2019-05-09): See my updated workaround below if you’re experiencing this problem with Terraform 0.12.0-rc1 or newer.

I was able to get as far as: ignore_changes = [stage[0].action[0].configuration]

However, I couldn’t figure out how to specifically ignore one attribute of configuration such as OAuthToken either.

Ignoring the entire configuration won’t work for my use case.

Even when I specify the GITHUB_TOKEN environment variable I still get the same issue as the OP. Is there something else you need to do as well?

Update for 0.12.0-rc1:

This is still broken in 0.12.0-rc1, but the workaround I posted a year ago (hacky birthday! 🎂) doesn’t work anymore.

You’ll first see an error saying “Dot must be followed by attribute name”, which can be fixed by using stage[0].action[0] instead of stage.0.action.0. That will fix the .OAuthToken portion, but the .configuration.% portion will not work. I also tried .configuration[%] and even tried incorporating the splat operator, but no dice there (“Splat expressions (.*) may not be used here.”).

The following approach will work in 0.12:

  # Workaround for Terraform insisting on "updating" OAuthToken every run. This
  # will prevent *any* updates to this CodePipeline resource while in place. In
  # the event that any real updates are needed, comment out the `lifecycle`
  # block, run `terraform apply`, and then restore the block.
  #
  # https://github.com/terraform-providers/terraform-provider-aws/issues/2854
  # https://www.terraform.io/docs/configuration/resources.html#ignore_changes
  lifecycle {
    ignore_changes = all
  }

NOTE: You could technically use ignore_changes = [stage] as well, which will allow you to update the CodePipeline resource itself as long as you don’t modify the stages. I prefer the all approach, because it will make it more obvious that something is wrong if I try to modify the resource itself and the stages. Using [stage] would allow top-level attribute changes to take place, while ignoring the changes to the stage block, which could lead to unpredictable results and an all-around bad time.

I had a look at the provider code and it seems that the OAuthToken is getting deleted from the state file.

I suspect this has been done to not store secrets in state file.

I suggest we change this behaviour and store the token in the state file and keep the experience consistent across resource.

@bflad @gdavison (please forward if someone else should be looking at the CodePipeline provider).

In the worst case, a hash of the OAuthToken could be stored in the state file so that we can do change-detection without having to expose the actual secret.

@sunilkumarmohanty if that is the case, then let’s just store the asterisk and move on. Who cares if it’s not an absolute truth, as long as it stops breaking expectations.

On further debugging, I found that the GetPipeline method of aws sdk for go returns **** instead of the actual OAuthToken, which means that the state file will always have **** in it instead of the actual OAuthToken. Hence, every time terraform plan is run, it will always state that the pipeline needs modification.

The solution proposed by @michaelmoussa is good, but it is not applicable when you are using the module which, in turn, creates the aws_codepipeline resource.

It is very inconvenient to change the source code of that module to comment/uncomment lifecycle block all the time (if you have a group of infrastructure engineers). And downright impossible if you have it published in GitHub.

Another solution is to use conditional resources i.e. count={var.force_github_token ? 1 : 0} but the problem is I already have 6 different codepipeline resources and that, in turn, will lead to having 12 codepipeline resources.