github.com/GoogleCloudPlatform/terraformer@v0.8.18/providers/aws/iam.go (about) 1 // Copyright 2018 The Terraformer Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package aws 16 17 import ( 18 "context" 19 "fmt" 20 "log" 21 "strings" 22 23 "github.com/GoogleCloudPlatform/terraformer/terraformutils" 24 25 "github.com/aws/aws-sdk-go-v2/service/iam" 26 "github.com/aws/aws-sdk-go-v2/service/iam/types" 27 ) 28 29 var IamAllowEmptyValues = []string{"tags."} 30 31 var IamAdditionalFields = map[string]interface{}{} 32 33 type IamGenerator struct { 34 AWSService 35 } 36 37 func (g *IamGenerator) InitResources() error { 38 config, e := g.generateConfig() 39 if e != nil { 40 return e 41 } 42 svc := iam.NewFromConfig(config) 43 g.Resources = []terraformutils.Resource{} 44 err := g.getUsers(svc) 45 if err != nil { 46 log.Println(err) 47 } 48 49 err = g.getGroups(svc) 50 if err != nil { 51 log.Println(err) 52 } 53 54 err = g.getPolicies(svc) 55 if err != nil { 56 log.Println(err) 57 } 58 59 err = g.getRoles(svc) 60 if err != nil { 61 log.Println(err) 62 } 63 64 err = g.getInstanceProfiles(svc) 65 if err != nil { 66 log.Println(err) 67 } 68 69 return nil 70 } 71 72 func (g *IamGenerator) getRoles(svc *iam.Client) error { 73 p := iam.NewListRolesPaginator(svc, &iam.ListRolesInput{}) 74 for p.HasMorePages() { 75 page, err := p.NextPage(context.TODO()) 76 if err != nil { 77 return err 78 } 79 for _, role := range page.Roles { 80 roleName := StringValue(role.RoleName) 81 g.Resources = append(g.Resources, terraformutils.NewSimpleResource( 82 roleName, 83 roleName, 84 "aws_iam_role", 85 "aws", 86 IamAllowEmptyValues)) 87 rolePoliciesPage := iam.NewListRolePoliciesPaginator(svc, &iam.ListRolePoliciesInput{RoleName: role.RoleName}) 88 for rolePoliciesPage.HasMorePages() { 89 rolePoliciesNextPage, err := rolePoliciesPage.NextPage(context.TODO()) 90 if err != nil { 91 log.Println(err) 92 continue 93 } 94 for _, policyName := range rolePoliciesNextPage.PolicyNames { 95 g.Resources = append(g.Resources, terraformutils.NewSimpleResource( 96 roleName+":"+policyName, 97 roleName+"_"+policyName, 98 "aws_iam_role_policy", 99 "aws", 100 IamAllowEmptyValues)) 101 } 102 } 103 roleAttachedPoliciesPage := iam.NewListAttachedRolePoliciesPaginator(svc, &iam.ListAttachedRolePoliciesInput{ 104 RoleName: &roleName, 105 }) 106 for roleAttachedPoliciesPage.HasMorePages() { 107 roleAttachedPoliciesNextPage, err := roleAttachedPoliciesPage.NextPage(context.TODO()) 108 if err != nil { 109 log.Println(err) 110 continue 111 } 112 for _, attachedPolicy := range roleAttachedPoliciesNextPage.AttachedPolicies { 113 g.Resources = append(g.Resources, terraformutils.NewResource( 114 roleName+"/"+*attachedPolicy.PolicyArn, 115 roleName+"_"+*attachedPolicy.PolicyName, 116 "aws_iam_role_policy_attachment", 117 "aws", 118 map[string]string{ 119 "role": roleName, 120 "policy_arn": *attachedPolicy.PolicyArn, 121 }, 122 IamAllowEmptyValues, 123 map[string]interface{}{})) 124 } 125 } 126 } 127 } 128 return nil 129 } 130 131 func (g *IamGenerator) getUsers(svc *iam.Client) error { 132 p := iam.NewListUsersPaginator(svc, &iam.ListUsersInput{}) 133 for p.HasMorePages() { 134 page, err := p.NextPage(context.TODO()) 135 if err != nil { 136 return err 137 } 138 for _, user := range page.Users { 139 resourceName := StringValue(user.UserName) 140 g.Resources = append(g.Resources, terraformutils.NewResource( 141 resourceName, 142 StringValue(user.UserId), 143 "aws_iam_user", 144 "aws", 145 map[string]string{ 146 "force_destroy": "false", 147 }, 148 IamAllowEmptyValues, 149 map[string]interface{}{})) 150 err := g.getUserPolices(svc, user.UserName) 151 if err != nil { 152 log.Println(err) 153 } 154 err = g.getUserPolicyAttachment(svc, user.UserName) 155 if err != nil { 156 log.Println(err) 157 } 158 err = g.getUserGroup(svc, user.UserName) 159 if err != nil { 160 log.Println(err) 161 } 162 } 163 } 164 return nil 165 } 166 167 func (g *IamGenerator) getUserGroup(svc *iam.Client, userName *string) error { 168 p := iam.NewListGroupsForUserPaginator(svc, &iam.ListGroupsForUserInput{UserName: userName}) 169 for p.HasMorePages() { 170 page, err := p.NextPage(context.TODO()) 171 if err != nil { 172 return err 173 } 174 for _, group := range page.Groups { 175 userGroupMembership := *userName + "/" + *group.GroupName 176 g.Resources = append(g.Resources, terraformutils.NewResource( 177 userGroupMembership, 178 userGroupMembership, 179 "aws_iam_user_group_membership", 180 "aws", 181 map[string]string{ 182 "user": *userName, 183 "groups.#": "1", 184 "groups.0": *group.GroupName, 185 }, 186 IamAllowEmptyValues, 187 IamAdditionalFields, 188 )) 189 } 190 } 191 return nil 192 } 193 194 func (g *IamGenerator) getUserPolices(svc *iam.Client, userName *string) error { 195 p := iam.NewListUserPoliciesPaginator(svc, &iam.ListUserPoliciesInput{UserName: userName}) 196 for p.HasMorePages() { 197 page, err := p.NextPage(context.TODO()) 198 if err != nil { 199 return err 200 } 201 for _, policy := range page.PolicyNames { 202 resourceName := StringValue(userName) + "_" + policy 203 resourceName = strings.ReplaceAll(resourceName, "@", "") 204 policyID := StringValue(userName) + ":" + policy 205 g.Resources = append(g.Resources, terraformutils.NewSimpleResource( 206 policyID, 207 resourceName, 208 "aws_iam_user_policy", 209 "aws", 210 IamAllowEmptyValues)) 211 } 212 } 213 return nil 214 } 215 216 func (g *IamGenerator) getUserPolicyAttachment(svc *iam.Client, userName *string) error { 217 p := iam.NewListAttachedUserPoliciesPaginator(svc, &iam.ListAttachedUserPoliciesInput{ 218 UserName: userName, 219 }) 220 for p.HasMorePages() { 221 page, err := p.NextPage(context.TODO()) 222 if err != nil { 223 return err 224 } 225 for _, attachedPolicy := range page.AttachedPolicies { 226 g.Resources = append(g.Resources, terraformutils.NewResource( 227 *userName+"/"+*attachedPolicy.PolicyArn, 228 *userName+"_"+*attachedPolicy.PolicyName, 229 "aws_iam_user_policy_attachment", 230 "aws", 231 map[string]string{ 232 "user": *userName, 233 "policy_arn": *attachedPolicy.PolicyArn, 234 }, 235 IamAllowEmptyValues, 236 map[string]interface{}{})) 237 } 238 } 239 return nil 240 } 241 242 func (g *IamGenerator) getPolicies(svc *iam.Client) error { 243 p := iam.NewListPoliciesPaginator(svc, &iam.ListPoliciesInput{Scope: types.PolicyScopeTypeLocal}) 244 for p.HasMorePages() { 245 page, err := p.NextPage(context.TODO()) 246 if err != nil { 247 return err 248 } 249 for _, policy := range page.Policies { 250 resourceName := StringValue(policy.PolicyName) 251 policyARN := StringValue(policy.Arn) 252 253 g.Resources = append(g.Resources, terraformutils.NewSimpleResource( 254 policyARN, 255 resourceName, 256 "aws_iam_policy", 257 "aws", 258 IamAllowEmptyValues)) 259 } 260 } 261 return nil 262 } 263 264 func (g *IamGenerator) getGroups(svc *iam.Client) error { 265 p := iam.NewListGroupsPaginator(svc, &iam.ListGroupsInput{}) 266 for p.HasMorePages() { 267 page, err := p.NextPage(context.TODO()) 268 if err != nil { 269 return err 270 } 271 for _, group := range page.Groups { 272 resourceName := StringValue(group.GroupName) 273 g.Resources = append(g.Resources, terraformutils.NewSimpleResource( 274 resourceName, 275 resourceName, 276 "aws_iam_group", 277 "aws", 278 IamAllowEmptyValues)) 279 g.getGroupPolicies(svc, group) 280 g.getAttachedGroupPolicies(svc, group) 281 } 282 } 283 return nil 284 } 285 286 func (g *IamGenerator) getGroupPolicies(svc *iam.Client, group types.Group) { 287 groupPoliciesPage := iam.NewListGroupPoliciesPaginator(svc, &iam.ListGroupPoliciesInput{GroupName: group.GroupName}) 288 for groupPoliciesPage.HasMorePages() { 289 groupPoliciesNextPage, err := groupPoliciesPage.NextPage(context.TODO()) 290 if err != nil { 291 log.Println(err) 292 continue 293 } 294 for _, policy := range groupPoliciesNextPage.PolicyNames { 295 id := *group.GroupName + ":" + policy 296 groupPolicyName := *group.GroupName + "_" + policy 297 g.Resources = append(g.Resources, terraformutils.NewResource( 298 id, 299 groupPolicyName, 300 "aws_iam_group_policy", 301 "aws", 302 map[string]string{}, 303 IamAllowEmptyValues, 304 IamAdditionalFields)) 305 } 306 } 307 } 308 309 func (g *IamGenerator) getAttachedGroupPolicies(svc *iam.Client, group types.Group) { 310 groupAttachedPoliciesPage := iam.NewListAttachedGroupPoliciesPaginator(svc, 311 &iam.ListAttachedGroupPoliciesInput{GroupName: group.GroupName}) 312 for groupAttachedPoliciesPage.HasMorePages() { 313 groupAttachedPoliciesNextPage, err := groupAttachedPoliciesPage.NextPage(context.TODO()) 314 if err != nil { 315 log.Println(err) 316 continue 317 } 318 for _, attachedPolicy := range groupAttachedPoliciesNextPage.AttachedPolicies { 319 if !strings.Contains(*attachedPolicy.PolicyArn, "arn:aws:iam::aws") { 320 continue // map only AWS managed policies since others should be managed by 321 } 322 id := *group.GroupName + "/" + *attachedPolicy.PolicyArn 323 g.Resources = append(g.Resources, terraformutils.NewResource( 324 id, 325 *group.GroupName+"_"+*attachedPolicy.PolicyName, 326 "aws_iam_group_policy_attachment", 327 "aws", 328 map[string]string{ 329 "group": *group.GroupName, 330 "policy_arn": *attachedPolicy.PolicyArn, 331 }, 332 IamAllowEmptyValues, 333 IamAdditionalFields)) 334 } 335 } 336 } 337 338 func (g *IamGenerator) getInstanceProfiles(svc *iam.Client) error { 339 p := iam.NewListInstanceProfilesPaginator(svc, &iam.ListInstanceProfilesInput{}) 340 for p.HasMorePages() { 341 page, err := p.NextPage(context.TODO()) 342 if err != nil { 343 return err 344 } 345 for _, instanceProfile := range page.InstanceProfiles { 346 resourceName := *instanceProfile.InstanceProfileName 347 348 g.Resources = append(g.Resources, terraformutils.NewResource( 349 resourceName, 350 resourceName, 351 "aws_iam_instance_profile", 352 "aws", 353 map[string]string{ 354 "name": resourceName, 355 }, 356 IamAllowEmptyValues, 357 IamAdditionalFields)) 358 } 359 } 360 return nil 361 } 362 363 // PostGenerateHook for add policy json as heredoc 364 func (g *IamGenerator) PostConvertHook() error { 365 for i, resource := range g.Resources { 366 switch { 367 case resource.InstanceInfo.Type == "aws_iam_policy" || 368 resource.InstanceInfo.Type == "aws_iam_user_policy" || 369 resource.InstanceInfo.Type == "aws_iam_group_policy" || 370 resource.InstanceInfo.Type == "aws_iam_role_policy": 371 policy := g.escapeAwsInterpolation(resource.Item["policy"].(string)) 372 resource.Item["policy"] = fmt.Sprintf(`<<POLICY 373 %s 374 POLICY`, policy) 375 case resource.InstanceInfo.Type == "aws_iam_role": 376 policy := g.escapeAwsInterpolation(resource.Item["assume_role_policy"].(string)) 377 g.Resources[i].Item["assume_role_policy"] = fmt.Sprintf(`<<POLICY 378 %s 379 POLICY`, policy) 380 case resource.InstanceInfo.Type == "aws_iam_instance_profile": 381 delete(resource.Item, "roles") 382 } 383 } 384 return nil 385 }