-
Notifications
You must be signed in to change notification settings - Fork 719
Description
⏺ ### Terraform CLI and Terraform IBM Provider Version
OpenTofu v1.x
provider registry.opentofu.org/ibm-cloud/ibm v1.86.1
Affected Resource(s)
ibm_resource_key
Terraform Configuration Files
resource "ibm_resource_key" "key" {
name = "my-cos-key"
role = "NONE"
resource_instance_id = ibm_resource_instance.instance.id
parameters = {
"HMAC" = true
}
} Debug Output
N/A
Panic Output
N/A
Expected Behavior
When importing an ibm_resource_key resource that was created with role = "NONE", the
Read function should populate role = "NONE" in the state, so that subsequent plans show
no diff and no replacement is required.
Actual Behavior
After importing an ibm_resource_key created with role = "NONE", the state has
role = null. On the next plan, Terraform detects a diff between the config
(role = "NONE") and the state (null) on a ForceNew field, and plans to destroy
and recreate the resource key. This silently rotates all HMAC credentials, breaking
any consumers relying on them.
Root Cause
In resourceIBMResourceKeyRead, the role field is only written to state inside the
if resourceKey.Credentials != nil && resourceKey.Credentials.IamRoleCRN != nil block:
// resource_ibm_resource_key.go, lines 372–401
if resourceKey.Credentials != nil && resourceKey.Credentials.IamRoleCRN != nil {
roleCrn := *resourceKey.Credentials.IamRoleCRN
// ... look up role name from CRN ...
d.Set("role", RoleName)
}
// no else branch — role is never set when IamRoleCRN is nil When a key is created with role = "NONE", no IAM role is assigned and IamRoleCRN
is nil in the API response. The entire block is skipped and d.Set("role", ...) is
never called.
During a normal Create flow this goes unnoticed: d is pre-populated with the
config value role = "NONE" before Create is called, and since SDK v2 preserves
Computed + Optional values not explicitly overwritten by Read, the state retains
"NONE" correctly.
During Import, d starts completely empty. Read is the only opportunity to populate
state from the API. Since IamRoleCRN is nil, role is never written and the state
ends up with null.
Steps to Reproduce
- Create an
ibm_resource_keywithrole = "NONE"viaterraform apply - Delete the resource from state:
terraform state rm ibm_resource_key.key - Add an
importblock pointing to the existing resource key CRN - Run
terraform plan - Observe
+ role = "NONE" # forces replacement— the resource is planned for destroy
and recreate despite the role being unchanged
Fix
Add an else branch to explicitly set role = "NONE" when IamRoleCRN is nil:
if resourceKey.Credentials != nil && resourceKey.Credentials.IamRoleCRN != nil {
roleCrn := *resourceKey.Credentials.IamRoleCRN
// ... existing role lookup logic ...
d.Set("role", RoleName)
} else {
d.Set("role", "NONE")
}This makes Read idempotent for both cases: keys with an IAM role (CRN present) and keys
with no IAM role ("NONE"). The IBM Cloud API confirms this behaviour: keys created with
role = "NONE" have no iam_role_crn in their credentials response, which is what
triggers the missing branch.
Important Factoids
The issue is specific to import. Resources created fresh via terraform apply are
unaffected because Terraform SDK v2 preserves Computed + Optional field values carried
over from the config through the Create → Read lifecycle. The bug only surfaces when the
prior state is absent, i.e. during import or after terraform state rm.
References
- IBM Cloud Resource Controller API — resource key credentials do not include
iam_role_crnwhen created with no IAM role ("NONE") - Terraform SDK v2 behaviour:
Computed + Optionalattributes retain prior state when
not set during Read