hashicorp / terraform-ls

Terraform Language Server
Mozilla Public License 2.0
985 stars 130 forks source link

Language server uses an ambiguous provider version when multiple .terraform environments exist #1664

Open seanhoughton opened 6 months ago

seanhoughton commented 6 months ago

Language Server Version

v0.32.8

Terraform Version

Terraform v1.7.3 on darwin_arm64

Client Version

VSCode 1.87.2

Terraform Configuration

terraform {
  required_providers {
    azuread = {
      source  = "hashicorp/azuread"
      version = "~> 2.47.0"
    }
  }
}

data "azuread_application_published_app_ids" "well_known" {}

Steps to Reproduce

  1. Install multiple versions of a terraform provider
  2. Hover over a resource title
  3. Observe the incorrect version of the provider being used

Alternatively, just hover over the provider to see the incorrect version number

Screenshot 2024-03-18 at 2 26 01β€―PM Screenshot 2024-03-18 at 2 27 36β€―PM

Expected Behavior

The language server should use the version of the provider set in the .terraform.lock.hcl or consider version constraints in the required_providers block.

Actual Behavior

The language server appears to pick a consistent (but sometimes incorrect) version from the ones that are installed.

Gist

No response

Workarounds

Delete all old versions of the provider from the .terraform/providers folder

References

No response

Help Wanted

Community Note

dbanck commented 6 months ago

Hi @seanhoughton,

Thanks for the report! I think I need more details to reproduce the problem. Can you please share your lockfile and the contents of your .terraform/providers directory?

I assume you ended up with multiple versions of the same provider through upgrades? Internally, the language server uses terraform provider schemas -json to get the schema for an installed provider. So we partially rely on Terraform to give us the correct schema, but we also parse the lockfile and configuration constraints. We use this information to pick the most appropriate schema version, either from disk or from our bundled schema.

seanhoughton commented 6 months ago

I just reproduced it again with only a single provider version installed in the plugins folder. It's a mystery where this provider is coming from. I've combed through the language server logs and can't find anything that shows where the provider plugins are coming from.

Screenshot 2024-03-19 at 9 18 45β€―AM

Here's the lockfile

# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.

provider "registry.terraform.io/hashicorp/azuread" {
  version     = "2.47.0"
  constraints = "~> 2.47.0"
  hashes = [
    "h1:g8+gBFM4QVOEQFqAEs5pR6iXpbGvgPvcEi1evHwziyw=",
    "zh:1372d81eb24ef3b4b00ea350fe87219f22da51691b8e42ce91d662f6c2a8af5e",
    "zh:1c3e89cf19118fc07d7b04257251fc9897e722c16e0a0df7b07fcd261f8c12e7",
    "zh:1e654a74d171d6ff8f9f6f67e3ff1421d4c5e56a18607703626bf12cd23ba001",
    "zh:35227fad617a0509c64ab5759a8b703b10d244877f1aa5416bfbcc100c96996f",
    "zh:357f553f0d78d46a96c7b2ed06d25ee0fc60fc5be19812ccb5d969fa47d62e17",
    "zh:58faa2940065137e3e87d02eba59ab5cd7137d7a18caf225e660d1788f274569",
    "zh:7308eda0339620fa24f47cedd22221fc2c02cab9d5be1710c09a783aea84eb3a",
    "zh:863eabf7f908a8263e28d8aa2ad1381affd6bb5c67755216781f674ef214100e",
    "zh:8b95b595a7c14ed7b56194d03cdec253527e7a146c1c58961be09e6b5c50baee",
    "zh:afbca6b4fac9a0a488bc22ff9e51a8f14e986137d25275068fd932f379a51d57",
    "zh:c6aadec4c81a44c3ffc22c2d90ffc6706bf5a9a903a395d896477516f4be6cbb",
    "zh:e54a59de7d4ef0f3a18f91fed0b54a2bce18257ae2ee1df8a88226e1023c5811",
  ]
}

provider "registry.terraform.io/hashicorp/random" {
  version = "3.6.0"
  hashes = [
    "h1:I8MBeauYA8J8yheLJ8oSMWqB0kovn16dF/wKZ1QTdkk=",
    "zh:03360ed3ecd31e8c5dac9c95fe0858be50f3e9a0d0c654b5e504109c2159287d",
    "zh:1c67ac51254ba2a2bb53a25e8ae7e4d076103483f55f39b426ec55e47d1fe211",
    "zh:24a17bba7f6d679538ff51b3a2f378cedadede97af8a1db7dad4fd8d6d50f829",
    "zh:30ffb297ffd1633175d6545d37c2217e2cef9545a6e03946e514c59c0859b77d",
    "zh:454ce4b3dbc73e6775f2f6605d45cee6e16c3872a2e66a2c97993d6e5cbd7055",
    "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
    "zh:91df0a9fab329aff2ff4cf26797592eb7a3a90b4a0c04d64ce186654e0cc6e17",
    "zh:aa57384b85622a9f7bfb5d4512ca88e61f22a9cea9f30febaa4c98c68ff0dc21",
    "zh:c4a3e329ba786ffb6f2b694e1fd41d413a7010f3a53c20b432325a94fa71e839",
    "zh:e2699bc9116447f96c53d55f2a00570f982e6f9935038c3810603572693712d0",
    "zh:e747c0fd5d7684e5bfad8aa0ca441903f15ae7a98a737ff6aca24ba223207e2c",
    "zh:f1ca75f417ce490368f047b63ec09fd003711ae48487fba90b4aba2ccf71920e",
  ]
}

provider "registry.terraform.io/hashicorp/vault" {
  version = "3.25.0"
  hashes = [
    "h1:3GN5k6zxDAI5gfcKENb/jnJyFGWA/0JoumnD6eyVZjs=",
    "zh:430308f5dbd322a2e5afafd2be810f44eb35e28afa0aa0ac30b270cd6f413da8",
    "zh:6c36da504c4af84ea9fbaf1e6c2560f691dc3d2d7f0b382a937bfae78830fa17",
    "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
    "zh:7bc39cb2a7d91631cb8be54b0b06de29fb47910923e54f779e74d8b218b1ab70",
    "zh:7e4a5bebcfa19b9f1e3a6bbda5c6771b6dd28b3dfa19fdf3d4fced419cfa416f",
    "zh:7ea473203b37d006a0d2b1cdc8bff55c96b3c5819dbb62862cdabff6f2f0e2f2",
    "zh:9ad136feece62f0c545fefa4592b2cdaa896a39acb697fb129233dce880a69aa",
    "zh:ad0c9980295c902804af23da0250830b912eb13089349bf5c7be0649fac2689c",
    "zh:b305835cc13dcd9ec996d49d23163c6311f30786296f86ca5657b93aea4f3591",
    "zh:d8fe6ab7da12efbb5b122ae9b6856375c5a3759add9df577a8fb448898ceffe3",
    "zh:ef59ef2c06a55571e64fdd5888a074ed9556436738e9737e32bacab93ca133ff",
    "zh:f59c2605d916e1806dc494241467dd430194f5e7bdbf331c5aca904873347ad8",
  ]
}

The schema looks correct. I've isolated the azuread_application_password resource because the schema is pretty big, and this highlights the problem. The application_object_id field was deprecated in a recent update of the azuread provider and exists in the schema output with "deprecated: true".



$ terraform providers schema -json | jq '.provider_schemas | ."registry.terraform.io/hashicorp/azuread" | .resource_schemas.azuread_application_password'

{
  "version": 1,
  "block": {
    "attributes": {
      "application_id": {
        "type": "string",
        "description": "The resource ID of the application for which this password should be created",
        "description_kind": "plain",
        "optional": true,
        "computed": true
      },
      "application_object_id": {
        "type": "string",
        "description": "The object ID of the application for which this password should be created",
        "description_kind": "plain",
        "deprecated": true,
        "optional": true,
        "computed": true
      },
      "display_name": {
        "type": "string",
        "description": "A display name for the password",
        "description_kind": "plain",
        "optional": true,
        "computed": true
      },
      "end_date": {
        "type": "string",
        "description": "The end date until which the password is valid, formatted as an RFC3339 date string (e.g. `2018-01-01T01:02:03Z`)",
        "description_kind": "plain",
        "optional": true,
        "computed": true
      },
      "end_date_relative": {
        "type": "string",
        "description": "A relative duration for which the password is valid until, for example `240h` (10 days) or `2400h30m`. Changing this field forces a new resource to be created",
        "description_kind": "plain",
        "optional": true
      },
      "id": {
        "type": "string",
        "description_kind": "plain",
        "optional": true,
        "computed": true
      },
      "key_id": {
        "type": "string",
        "description": "A UUID used to uniquely identify this password credential",
        "description_kind": "plain",
        "computed": true
      },
      "rotate_when_changed": {
        "type": [
          "map",
          "string"
        ],
        "description": "Arbitrary map of values that, when changed, will trigger rotation of the password",
        "description_kind": "plain",
        "optional": true
      },
      "start_date": {
        "type": "string",
        "description": "The start date from which the password is valid, formatted as an RFC3339 date string (e.g. `2018-01-01T01:02:03Z`). If this isn't specified, the current date is used",
        "description_kind": "plain",
        "optional": true,
        "computed": true
      },
      "value": {
        "type": "string",
        "description": "The password for this application, which is generated by Azure Active Directory",
        "description_kind": "plain",
        "computed": true,
        "sensitive": true
      }
    },
    "block_types": {
      "timeouts": {
        "nesting_mode": "single",
        "block": {
          "attributes": {
            "create": {
              "type": "string",
              "description_kind": "plain",
              "optional": true
            },
            "delete": {
              "type": "string",
              "description_kind": "plain",
              "optional": true
            },
            "read": {
              "type": "string",
              "description_kind": "plain",
              "optional": true
            },
            "update": {
              "type": "string",
              "description_kind": "plain",
              "optional": true
            }
          },
          "description_kind": "plain"
        }
      }
    },
    "description_kind": "plain"
  }
}```
dbanck commented 6 months ago

Thanks for the additional details!

We have two sources for provider schemas:

The latter should always be preferred over the bundled one, as it is a better match to the provider version in use.

The language server build 0.32.8 shipped with azuread 2.47.0, so I'm also confused where 2.41.0 came from. Can you double check your language server version? It is listed at the top of the log output:

Launching language server: /Users/dbanck/Projects/terraform-ls/terraform-ls serve
Client: Stopped --> Starting
2024/03/19 14:21:09 serve_command.go:108: Starting terraform-ls 0.32.8
2024/03/19 14:21:09 service.go:105: Preparing new session ...

Are there other .terraform directories in your workspace (the root folder you opened in VS Code) with a different azuread version?

seanhoughton commented 6 months ago

I'm using the 32.8 language server, but I think I figured it out. I'm using three different environments in the project. All of the environments share a set of modules but each have their own .terraform state. The error I'm seeing is only when I'm in a file in the /modules folder which has an ambiguous provider version if the prod and staging environments use different provider configurations. I had originally suspected this and updated two environments but I just noticed that there's a third one in there which uses 2.41.0 instead of 2.47.0 - so that solves the mystery of where 2.41.0 came from.

I'm not sure what the fix should be. It could be a workspace configuration setting similar to the "select the python virtual environment" option in the Python extension. Ultimately all the environments should be on the same version so I have resolved this by ensuring all environments are on the same provider revisions.

.
β”œβ”€β”€ env
β”‚Β Β  β”œβ”€β”€ prod
β”‚Β Β  β”‚Β Β  └── .terraform
β”‚Β Β  β”‚Β Β      └── providers
β”‚Β Β  └── staging
β”‚Β Β      └── .terraform
β”‚Β Β          └── providers
└── modules
    β”œβ”€β”€ a
    β”œβ”€β”€ b
    └── c