kubernetes/kops · error

error parsing actual RolePolicyDocument for IAMRole %q: %v

Error message

error parsing actual RolePolicyDocument for IAMRole %q: %v

What it means

Find() also parses the actual (AWS-returned, unescaped) RolePolicyDocument as JSON. If the live trust policy on the role is not valid JSON, the error is wrapped with the role name. Since AWS normally stores valid JSON, this signals the document was mutated by something non-standard.

Source

Thrown at upup/pkg/fi/cloudup/awstasks/iamrole.go:108

			return nil, fmt.Errorf("error parsing AssumeRolePolicyDocument for IAMRole %s: %v", *e.Name, err)
		}

		// The RolePolicyDocument is reformatted by AWS
		// We parse both as JSON; if the json forms are equal we pretend the actual value is the expected value
		if e.RolePolicyDocument != nil {
			expectedPolicy, err := fi.ResourceAsString(e.RolePolicyDocument)
			if err != nil {
				return nil, fmt.Errorf("error reading expected RolePolicyDocument for IAMRole %q: %v", aws.ToString(e.Name), err)
			}
			expectedJson := make(map[string]interface{})
			err = json.Unmarshal([]byte(expectedPolicy), &expectedJson)
			if err != nil {
				return nil, fmt.Errorf("error parsing expected RolePolicyDocument for IAMRole %q: %v", aws.ToString(e.Name), err)
			}
			actualJson := make(map[string]interface{})
			err = json.Unmarshal([]byte(actualPolicy), &actualJson)
			if err != nil {
				return nil, fmt.Errorf("error parsing actual RolePolicyDocument for IAMRole %q: %v", aws.ToString(e.Name), err)
			}

			if reflect.DeepEqual(actualJson, expectedJson) {
				klog.V(2).Infof("actual RolePolicyDocument was json-equal to expected; returning expected value")
				actualPolicy = expectedPolicy
			}
		}

		actual.RolePolicyDocument = fi.NewStringResource(actualPolicy)
	}
	actual.Tags = mapIAMTagsToMap(r.Tags)

	klog.V(2).Infof("found matching IAMRole %q", aws.ToString(actual.ID))
	e.ID = actual.ID

	// Avoid spurious changes
	actual.ExportWithID = e.ExportWithID
	actual.Lifecycle = e.Lifecycle

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Inspect the live policy with aws iam get-role and fix or replace it with valid JSON
  2. Re-apply the desired policy from the kOps spec (kops update cluster --yes)
  3. Avoid manual edits to the trust policy; manage it solely through kOps
  4. If an external tool manages the role, reconcile its output format with kOps
Defensive patterns

Strategy: validation

Validate before calling

// validate the live policy decodes and parses before comparison
raw, _ := url.QueryUnescape(*r.AssumeRolePolicyDocument)
var v interface{}
if err := json.Unmarshal([]byte(raw), &v); err != nil {
    // drift detected: live trust policy is not valid JSON
}

Type guard

func livePolicyIsJSON(encoded string) bool {
    raw, err := url.QueryUnescape(encoded)
    if err != nil { return false }
    var v interface{}
    return json.Unmarshal([]byte(raw), &v) == nil
}

Prevention

When it happens

Trigger: json.Unmarshal([]byte(actualPolicy), &actualJson) fails on the URL-unescaped AssumeRolePolicyDocument returned by GetRole.

Common situations: External tools (scripts, IaC drift) wrote a malformed trust policy directly to the role; double-encoding produced garbage after unescape; partial manual edits via console in raw mode.

Related errors


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/201469e1ab52ea36. Report an issue: GitHub.