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  }