{"record":{"id":"201469e1ab52ea36","repo":"kubernetes/kops","slug":"error-parsing-actual-rolepolicydocument-for-iamrol","errorCode":null,"errorMessage":"error parsing actual RolePolicyDocument for IAMRole %q: %v","messagePattern":"error parsing actual RolePolicyDocument for IAMRole %q: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/cloudup/awstasks/iamrole.go","lineNumber":108,"sourceCode":"\t\t\treturn nil, fmt.Errorf(\"error parsing AssumeRolePolicyDocument for IAMRole %s: %v\", *e.Name, err)\n\t\t}\n\n\t\t// The RolePolicyDocument is reformatted by AWS\n\t\t// We parse both as JSON; if the json forms are equal we pretend the actual value is the expected value\n\t\tif e.RolePolicyDocument != nil {\n\t\t\texpectedPolicy, err := fi.ResourceAsString(e.RolePolicyDocument)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error reading expected RolePolicyDocument for IAMRole %q: %v\", aws.ToString(e.Name), err)\n\t\t\t}\n\t\t\texpectedJson := make(map[string]interface{})\n\t\t\terr = json.Unmarshal([]byte(expectedPolicy), &expectedJson)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error parsing expected RolePolicyDocument for IAMRole %q: %v\", aws.ToString(e.Name), err)\n\t\t\t}\n\t\t\tactualJson := make(map[string]interface{})\n\t\t\terr = json.Unmarshal([]byte(actualPolicy), &actualJson)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error parsing actual RolePolicyDocument for IAMRole %q: %v\", aws.ToString(e.Name), err)\n\t\t\t}\n\n\t\t\tif reflect.DeepEqual(actualJson, expectedJson) {\n\t\t\t\tklog.V(2).Infof(\"actual RolePolicyDocument was json-equal to expected; returning expected value\")\n\t\t\t\tactualPolicy = expectedPolicy\n\t\t\t}\n\t\t}\n\n\t\tactual.RolePolicyDocument = fi.NewStringResource(actualPolicy)\n\t}\n\tactual.Tags = mapIAMTagsToMap(r.Tags)\n\n\tklog.V(2).Infof(\"found matching IAMRole %q\", aws.ToString(actual.ID))\n\te.ID = actual.ID\n\n\t// Avoid spurious changes\n\tactual.ExportWithID = e.ExportWithID\n\tactual.Lifecycle = e.Lifecycle","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/cloudup/awstasks/iamrole.go#L90-L126","documentation":"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.","triggerScenarios":"json.Unmarshal([]byte(actualPolicy), &actualJson) fails on the URL-unescaped AssumeRolePolicyDocument returned by GetRole.","commonSituations":"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.","solutions":["Inspect the live policy with aws iam get-role and fix or replace it with valid JSON","Re-apply the desired policy from the kOps spec (kops update cluster --yes)","Avoid manual edits to the trust policy; manage it solely through kOps","If an external tool manages the role, reconcile its output format with kOps"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// validate the live policy decodes and parses before comparison\nraw, _ := url.QueryUnescape(*r.AssumeRolePolicyDocument)\nvar v interface{}\nif err := json.Unmarshal([]byte(raw), &v); err != nil {\n    // drift detected: live trust policy is not valid JSON\n}","typeGuard":"func livePolicyIsJSON(encoded string) bool {\n    raw, err := url.QueryUnescape(encoded)\n    if err != nil { return false }\n    var v interface{}\n    return json.Unmarshal([]byte(raw), &v) == nil\n}","tryCatchPattern":null,"preventionTips":["Manage trust policies exclusively through kOps to prevent drift","Audit roles for out-of-band edits (config drift detection)","Re-apply the desired policy when live JSON becomes invalid","Block console raw-edit workflows for kOps-managed roles"],"tags":["aws","iam","json","drift","policy"],"backgroundTag":"invalid-json-policy","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T07:17:12.445Z"}