hashicorp / terraform-provider-aws

The AWS Provider enables Terraform to manage AWS resources.
https://registry.terraform.io/providers/hashicorp/aws
Mozilla Public License 2.0
9.76k stars 9.12k forks source link

It tries to update the Redshift parameter group every time #19110

Open safeith opened 3 years ago

safeith commented 3 years ago

I am using the following simple code

# wlm.json
[
  {
    "query_concurrency": 15,
    "query_group": [],
    "query_group_wild_card": 0,
    "user_group": [],
    "user_group_wild_card": 0,
    "auto_wlm": true,
    "concurrency_scaling": "auto",
    "rules": [
      {
        "rule_name": "rule_query_execution",
        "predicate": [
          {
            "metric_name": "query_execution_time",
            "operator": ">",
            "value": 10000
          }
        ],
        "action": "abort"
      }
    ]
  },
  {
    "short_query_queue": true
  }
]
# main.tf
resource "aws_redshift_parameter_group" "foo" {
  name        = "foo-parameter-group"
  family      = "redshift-1.0"
  description = "foo parameter group"

  parameter {
    name  = "wlm_json_configuration"
    value = jsonencode(jsondecode(file("./wlm.json")))
  }
}

I run

terraform init

Initializing the backend...

Initializing provider plugins...
- Using previously-installed hashicorp/aws v3.37.0

The following providers do not have any version constraints in configuration,
so the latest version was installed.

To prevent automatic upgrades to new major versions that may contain breaking
changes, we recommend adding version constraints in a required_providers block
in your configuration, with the constraint strings suggested below.

* hashicorp/aws: version = "~> 3.37.0"

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

and

terraform apply

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_redshift_parameter_group.foo will be created
  + resource "aws_redshift_parameter_group" "foo" {
      + arn         = (known after apply)
      + description = "foo parameter group"
      + family      = "redshift-1.0"
      + id          = (known after apply)
      + name        = "foo-parameter-group"

      + parameter {
          + name  = "wlm_json_configuration"
          + value = jsonencode(
                [
                  + {
                      + auto_wlm              = true
                      + concurrency_scaling   = "auto"
                      + query_concurrency     = 15
                      + query_group           = []
                      + query_group_wild_card = 0
                      + rules                 = [
                          + {
                              + action    = "abort"
                              + predicate = [
                                  + {
                                      + metric_name = "query_execution_time"
                                      + operator    = ">"
                                      + value       = 10000
                                    },
                                ]
                              + rule_name = "rule_query_execution"
                            },
                        ]
                      + user_group            = []
                      + user_group_wild_card  = 0
                    },
                  + {
                      + short_query_queue = true
                    },
                ]
            )
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_redshift_parameter_group.foo: Creating...
aws_redshift_parameter_group.foo: Creation complete after 1s [id=foo-parameter-group]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Till here it's ok but when I run it again it wants to change resource!

terraform apply
aws_redshift_parameter_group.foo: Refreshing state... [id=foo-parameter-group]

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # aws_redshift_parameter_group.foo will be updated in-place
  ~ resource "aws_redshift_parameter_group" "foo" {
        arn         = "arn:aws:redshift:eu-west-1:**************:parametergroup:foo-parameter-group"
        description = "foo parameter group"
        family      = "redshift-1.0"
        id          = "foo-parameter-group"
        name        = "foo-parameter-group"
        tags        = {}

      - parameter {
          - name  = "wlm_json_configuration" -> null
          - value = jsonencode(
                [
                  - {
                      - auto_wlm              = true
                      - concurrency_scaling   = "auto"
                      - query_concurrency     = 15
                      - query_group           = []
                      - query_group_wild_card = 0
                      - rules                 = [
                          - {
                              - action    = "abort"
                              - predicate = [
                                  - {
                                      - metric_name = "query_execution_time"
                                      - operator    = ">"
                                      - value       = 10000
                                    },
                                ]
                              - rule_name = "rule_query_execution"
                            },
                        ]
                      - user_group            = []
                      - user_group_wild_card  = 0
                    },
                  - {
                      - short_query_queue = true
                    },
                ]
            ) -> null
        }
      + parameter {
          + name  = "wlm_json_configuration"
          + value = jsonencode(
                [
                  + {
                      + auto_wlm              = true
                      + concurrency_scaling   = "auto"
                      + query_concurrency     = 15
                      + query_group           = []
                      + query_group_wild_card = 0
                      + rules                 = [
                          + {
                              + action    = "abort"
                              + predicate = [
                                  + {
                                      + metric_name = "query_execution_time"
                                      + operator    = ">"
                                      + value       = 10000
                                    },
                                ]
                              + rule_name = "rule_query_execution"
                            },
                        ]
                      + user_group            = []
                      + user_group_wild_card  = 0
                    },
                  + {
                      + short_query_queue = true
                    },
                ]
            )
        }
    }

Plan: 0 to add, 1 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value:

Terraform version: v0.13.5 Aws provider: aws v3.37.0

gdavison commented 3 years ago

Thanks for raising this, @safeith. Could you tell me what happens if you remove the jsonencode(jsondecode(...)) part, so that the parameters are

  parameter {
    name  = "wlm_json_configuration"
    value = file("./wlm.json")
  }

Decoding and re-encoding might be introducing changes

safeith commented 3 years ago

@gdavison Thanks for piking this up, It gives the following error

terraform apply

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_redshift_parameter_group.foo will be created
  + resource "aws_redshift_parameter_group" "foo" {
      + arn         = (known after apply)
      + description = "foo parameter group"
      + family      = "redshift-1.0"
      + id          = (known after apply)
      + name        = "foo-parameter-group"

      + parameter {
          + name  = "wlm_json_configuration"
          + value = jsonencode(
                [
                  + {
                      + auto_wlm              = true
                      + concurrency_scaling   = "auto"
                      + query_concurrency     = 15
                      + query_group           = []
                      + query_group_wild_card = 0
                      + rules                 = [
                          + {
                              + action    = "abort"
                              + predicate = [
                                  + {
                                      + metric_name = "query_execution_time"
                                      + operator    = ">"
                                      + value       = 10000
                                    },
                                ]
                              + rule_name = "rule_query_execution"
                            },
                        ]
                      + user_group            = []
                      + user_group_wild_card  = 0
                    },
                  + {
                      + short_query_queue = true
                    },
                ]
            )
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_redshift_parameter_group.foo: Creating...

Error: error adding Redshift Parameter Group (foo-parameter-group) parameters: InvalidParameterValue: Invalid value for wlm_json_configuration
    status code: 400, request id: 8b49c2f7-2c27-4fe1-af02-ec8a4d414f8a

  on main.tf line 1, in resource "aws_redshift_parameter_group" "foo":
   1: resource "aws_redshift_parameter_group" "foo" {

And I use jsonencode(jsondecode(...)) because I found it in this post https://github.com/hashicorp/terraform-provider-aws/issues/7865#issuecomment-613787528 nad it fixes this error

jmreicha commented 3 years ago

Seeing this as well, haven't been able to find a workaround.

JihadMotii-REISys commented 3 years ago

Any updates on this? i'm having the same exactly issue as described above. Thanks!

JihadMotii-REISys commented 3 years ago

I've tried this solution and it seems to be working for now


    name  = "wlm_json_configuration"
    value = jsonencode(jsondecode(templatefile("${path.module}/wlm.json.tpl", {})))
  }
safeith commented 3 years ago

I've tried this solution and it seems to be working for now

    name  = "wlm_json_configuration"
    value = jsonencode(jsondecode(templatefile("${path.module}/wlm.json.tpl", {})))
  }

Unfortunately, it does not work in my case. Could you please the versions that you are testing with?

kostyaplis commented 3 years ago

I have the same issue for quite some time. I have multiple almost identical redshift clusters in different environments. WLM configuration is 100% identical for all of them. It seems that issue doesn't exists in environments that were created earlier (>9 months ago), but exists on recently created environments. Even though all the environments got recent updates including WLM JSON configuration.

Tested right now with: Terraform 1.0.0 hashicorp/aws v3.47.0

2 older environments doesn't generate any change set (even though WLM config was recently updated) 2 newer environments (1 created today) are constantly suggesting to update wlm_json_configuration

Checked that tfstate is identical across all the environments

The only differnce I've noticed was running aws redshift describe-cluster-parameters --parameter-group-name my-custom-config:

Do note on different ApplyType

Healthy:

{
      "ParameterName": "wlm_json_configuration",
      "ParameterValue": "["**REDACTED FOR BREVITY**"]",
      "Description": "wlm json configuration",
      "Source": "user",
      "DataType": "string",
      "ApplyType": "dynamic",
      "IsModifiable": true
    }

Faulty:

{
      "ParameterName": "wlm_json_configuration",
      "ParameterValue": "["**REDACTED FOR BREVITY**"]",
      "Description": "wlm json configuration",
      "Source": "user",
      "DataType": "string",
      "ApplyType": "static",
      "IsModifiable": true
    }

Hope this helps

P.S....I think those older envs with "ApplyType": "dynamic" were manually modified at some stage(via cli or console), so ApplyType got changed. Not sure if ApplyType is the reason for TF changeset, but it is the only difference I could found between faulty and healthy envs.

kostyaplis commented 3 years ago

With further tests I found that issue exists if there is a WLM rule with < or > operators. \\u003e or \\u003c in tfstate. Replaced those with = and issue is gone. Revert back to > and constantly getting changeset again.

Still cannot explain why that happens only in some of my environments.

yannanyin commented 3 years ago

any update?..

keweishang commented 3 years ago

We also have the same observation that if we have < or > operators, the issue exists, if we replace it with =, the issue is gone.

For example, the following configuration will have the same issue:

        "rules": [
            {
                "rule_name": "rule_1",
                "predicate": [
                    {
                        "metric_name": "spectrum_scan_size_mb",
                        "operator": ">",
                        "value": 1
                    }
                ],
                "action": "abort"
            }
        ],

However, if we replace > with =, the issue is gone.

Any workaround? Thanks.

Terraform v0.15.5 Aws provider: v3.38.0

keweishang commented 3 years ago

Can you try

    parameter {
        # Main WLM configuration
        name = "wlm_json_configuration"
        value = replace(replace(jsonencode(jsondecode(file("${path.module}/wlm.json"))),"\\u003e", ">"),"\\u003c", "<")
    }

It is a workaround that works for us for the < and > case.

It looks like the jsonencode function replaces > with \u003e. Reference from the doc:

When encoding strings, this function escapes some characters using Unicode escape sequences: replacing <, >, &, U+2028, and U+2029 with \u003c, \u003e, \u0026, \u2028, and \u2029. This is to preserve compatibility with Terraform 0.11 behavior.

jmreicha commented 3 years ago

@keweishang tried the workaround you suggested, but didn’t have any luck.

mlearl commented 3 years ago

I see a similar issue. If I encode the WLM as a string literal in the exact format dumped by

aws redshift describe-cluster-parameters --parameter-group-name [group name]

it all works as expected, but anything else (including, as far as I can tell, putting the exact same string in a file and referencing it with file() !?) has the result that a) it never reports as matching, even when the plan shows it being updated to an identical value, and b) it never applies, always failing with "InvalidParameterValue: Invalid JSON for wlm_json_configuration".

Update: Ok, JSON from file works as long as there's no whitespace in the JSON. I deleted the non-default queues on the cluster manually and then was able to apply the update, and can no longer reproduce this error. (Also, queue names seem to be case-preserving but not case-sensitive?, which was a bit confusing to me initially).

enochlo commented 2 years ago

We ran into a similar issue with .json files for configuring WLM, but it seems like the issue was with \n characters being present in the json.

We tried a replace(..., "\n", " ") and it solved our issues.

mlearl commented 2 years ago

We ran into a similar issue with .json files for configuring WLM, but it seems like the issue was with \n characters being present in the json.

We tried a replace(..., "\n", " ") and it solved our issues.

What's working now for us is the jsonencode(jsondecode(file])) trick mentioned above (to get it in a particular friendly encoding, I guess), but I'm almost? sure that didn't work for us originally, there's definitely something funny going on with queue name case and/or certain kinds of special characters.

castironclay commented 2 years ago

I had success finally using wlm_json_configuration = jsonencode([{"query_concurrency":5}])

I would query with awscli then use that to make my single line json minue the '\' escape characters: aws redshift describe-cluster-parameters --parameter-group-name mygroup Output: { "ParameterName": "wlm_json_configuration", "ParameterValue": "[{\"query_concurrency\":5}]", "Description": "wlm json configuration", "Source": "user", "DataType": "string", "ApplyType": "static", "IsModifiable": true }

farfromunique commented 2 years ago

It looks like the issue may be tied to unexpected white space. People are saying that the issue seems resolved when removing "\n", and when copy/pasting the value from the CLI. In my case, I had set my variable to:

variable "wlm_json_configuration" {
  type    = string
  default = "[{\"query_concurrency\": 5}]" # Note the space between `:` and `5`
}

When I removed the space, it stopped saying that there was a change:

      - parameter {
          - name  = "wlm_json_configuration" -> null
          - value = jsonencode(
                [
                  - {
                      - query_concurrency = 5
                    },
                ]
            ) -> null
        }
      + parameter {
          + name  = "wlm_json_configuration"
          + value = jsonencode(
                [
                  + {
                      + query_concurrency = 5
                    },
                ]
            )
        }

This is a hard issue to see, because the format of TF's output "decompresses" the JSON so that it isn't what we typed. Possible solutions:

zachBridges commented 2 years ago

For folks who are looking for a workaround to this issue, let me echo what others have suggested as it worked for me.

After applying my change via terraform, I retrieved the JSON string for ParameterValue via the aws cli as @castironclay mentioned above. Omitting my usage of jsonencode and using the raw JSON string resulted in terraform no longer treating the value as different upon each terraform plan.

E.x., value = "[{\"auto_wlm\":true,\"rules\":[{\"action\":\"abort\",\"predicate\":[{\"metric_name\":\"query_execution_time\",\"operator\":\">\",\"value\":14400}],\"rule_name\":\"

aakilin commented 12 months ago

@farfromunique it works for me either, thank you very much!

halfinhalfout commented 4 months ago

@keweishang Your value = replace(replace(jsonencode(jsondecode(file("${path.module}/wlm.json"))),"\\u003e", ">"),"\\u003c", "<") worked a charm for me. Thank you!

alexGitSpace commented 5 days ago

For folks who are looking for a workaround to this issue, let me echo what others have suggested as it worked for me.

After applying my change via terraform, I retrieved the JSON string for ParameterValue via the aws cli as @castironclay mentioned above. Omitting my usage of jsonencode and using the raw JSON string resulted in terraform no longer treating the value as different upon each terraform plan.

E.x., value = "[{\"auto_wlm\":true,\"rules\":[{\"action\":\"abort\",\"predicate\":[{\"metric_name\":\"query_execution_time\",\"operator\":\">\",\"value\":14400}],\"rule_name\":\"

@zachBridges, works for me as well. Thanks!