func (i *IAM) CreateRole(ctx context.Context, req IAMRoleRequest) (*iam.CreateRoleOutput, error) {
	log := log.Logger(ctx, "awsapi", "iam", "CreateRole")
	log = log.WithValues("roleName", req.Name)
	input := &iam.CreateRoleInput{
		AssumeRolePolicyDocument: aws.String(req.TrustPolicy),
		RoleName:                 aws.String(req.Name),
		Description:              aws.String(req.Description),
		MaxSessionDuration:       aws.Int64(req.SessionDuration),
		PermissionsBoundary:      aws.String(req.ManagedPermissionBoundaryPolicy),
	}
	if err := input.Validate(); err != nil {
		log.Error(err, "input validation failed")
		return nil, err
	}
	log.V(1).Info("Initiating api call")
	resp, err := i.Client.CreateRole(input)
	if err != nil {
		if aerr, ok := err.(awserr.Error); ok {
			switch aerr.Code() {
			case iam.ErrCodeEntityAlreadyExistsException:
				log.Info(iam.ErrCodeEntityAlreadyExistsException)
			case iam.ErrCodeLimitExceededException:
				log.Error(err, iam.ErrCodeLimitExceededException)
			case iam.ErrCodeNoSuchEntityException:
				log.Error(err, iam.ErrCodeNoSuchEntityException)
			case iam.ErrCodeServiceFailureException:
				log.Error(err, iam.ErrCodeServiceFailureException)
			default:
				log.Error(err, aerr.Error())
			}
		}
		return nil, err
	}
	log.V(1).Info("Successfully created the role")
	return resp, nil
}
CreateRole will try to create an IAM Role, or return back Nil if it can not be created
package awsapi
import (
	"context"
	"fmt"
	"strings"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/iam"
	"github.com/aws/aws-sdk-go/service/iam/iamiface"
	"github.com/keikoproj/iam-manager/pkg/log"
)
type IAMIface interface {
	CreateRole(ctx context.Context, req IAMRoleRequest)
	EnsureRole(ctx context.Context, req IAMRoleRequest)
	UpdateRole(ctx context.Context, req IAMRoleRequest)
	DeleteRole(ctx context.Context, roleName string)
	GetRole(ctx context.Context, roleName string)
	AttachInlineRolePolicy(ctx context.Context, req IAMRoleRequest)
	AddPermissionBoundary(ctx context.Context, req IAMRoleRequest) error
	GetRolePolicy(ctx context.Context, req IAMRoleRequest) bool
}
const (
	RoleExistsAlreadyForOtherNamespace = "Please choose a different name"
)
type IAMRoleRequest struct {
	Name                            string
	PolicyName                      string
	Description                     string
	SessionDuration                 int64
	TrustPolicy                     string
	PermissionPolicy                string
	ManagedPermissionBoundaryPolicy string
	ManagedPolicies                 []string
	Tags                            map[string]string
}
type IAMRoleResponse struct {
	RoleARN string
	RoleID  string
}
func NewIAMRoleResponseFromGetRole(output iam.GetRoleOutput) *IAMRoleResponse {
	return &IAMRoleResponse{
		RoleARN: aws.StringValue(output.Role.Arn),
		RoleID:  aws.StringValue(output.Role.RoleId),
	}
}
func NewIAMRoleResponseFromCreateRole(output iam.CreateRoleOutput) *IAMRoleResponse {
	return &IAMRoleResponse{
		RoleARN: aws.StringValue(output.Role.Arn),
		RoleID:  aws.StringValue(output.Role.RoleId),
	}
}
type IAM struct {
	Client iamiface.IAMAPI
}
func NewIAM(region string) *IAM {
	sess, err := session.NewSession(&aws.Config{Region: aws.String(region)})
	if err != nil {
		panic(err)
	}
	return &IAM{
		Client: iam.New(sess),
	}
}
func (i *IAM) EnsureRole(ctx context.Context, req IAMRoleRequest) (*IAMRoleResponse, error) {
	log := log.Logger(ctx, "awsapi", "iam", "EnsureRole")
	log = log.WithValues("roleName", req.Name)
	log.V(1).Info("Verifying that IAM Role exists")
	role, err := i.GetOrCreateRole(ctx, req)
	if err != nil {
		return &IAMRoleResponse{}, err
	}
	log.V(1).Info("Verifying Tags")
	_, err = i.VerifyTags(ctx, req)
	if err != nil {
		return &IAMRoleResponse{}, err
	}
	log.V(1).Info("Attaching Tag")
	_, err = i.TagRole(ctx, req)
	if err != nil {
		return &IAMRoleResponse{}, err
	}
	log.V(1).Info("Attaching Permission Boundary")
	err = i.AddPermissionBoundary(ctx, req)
	if err != nil {
		return &IAMRoleResponse{}, err
	}
	log.V(1).Info("Attaching Managed policies")
	for _, policy := range req.ManagedPolicies {
		if policy != "" {
			err = i.AttachManagedRolePolicy(ctx, policy, req.Name)
			if err != nil {
				log.Error(err, "Error while attaching managed policy", "policy", policy)
				return &IAMRoleResponse{}, err
			}
		}
	}
	log.V(1).Info("Attaching Inline role policies")
	_, err = i.UpdateRole(ctx, req)
	if err != nil {
		log.Error(err, "Error while updating role")
		return &IAMRoleResponse{}, err
	}
	return role, nil
}
func (i *IAM) GetOrCreateRole(ctx context.Context, req IAMRoleRequest) (*IAMRoleResponse, error) {
	log := log.Logger(ctx, "awsapi", "iam", "GetOrCreateRole")
	log = log.WithValues("roleName", req.Name)
	getResp, err := i.GetRole(ctx, req)
	if err != nil {
		if aerr, ok := err.(awserr.Error); ok {
			switch aerr.Code() {
			case iam.ErrCodeNoSuchEntityException:
				log.Info("Role does not already exist")
			default:
				return nil, err
			}
		}
	}
	if getResp != nil {
		return NewIAMRoleResponseFromGetRole(*getResp), nil
	}
	createResp, err := i.CreateRole(ctx, req)
	if err != nil {
		return nil, err
	}
	if createResp != nil {
		return NewIAMRoleResponseFromCreateRole(*createResp), nil
	}
	return nil, nil
}
func (i *IAM) VerifyTags(ctx context.Context, req IAMRoleRequest) (*IAMRoleResponse, error) {
	log := log.Logger(ctx, "awsapi", "iam", "VerifyTags")
	log = log.WithValues("roleName", req.Name)
	log.V(1).Info("Initiating api call")
	listTags, err := i.Client.ListRoleTags(&iam.ListRoleTagsInput{
		RoleName: aws.String(req.Name),
	})
	if err != nil {
		return nil, err
	}
	flag := false
	for _, tag := range listTags.Tags {
		if aws.StringValue(tag.Key) == "Namespace" {
			if aws.StringValue(tag.Value) != req.Tags["Namespace"] {
				flag = true
				break
			}
		}
		if aws.StringValue(tag.Key) == "Cluster" {
			if aws.StringValue(tag.Value) != req.Tags["Cluster"] {
				flag = true
				break
			}
		}
	}
	if flag {
		return nil, fmt.Errorf("role name %s in AWS is not available. %s", req.Name, RoleExistsAlreadyForOtherNamespace)
	}
	return &IAMRoleResponse{}, nil
}
func (i *IAM) TagRole(ctx context.Context, req IAMRoleRequest) (*IAMRoleResponse, error) {
	log := log.Logger(ctx, "awsapi", "iam", "TagRole")
	log = log.WithValues("roleName", req.Name)
	log.V(1).Info("Initiating api call")
	var tags []*iam.Tag
	for k, v := range req.Tags {
		tag := &iam.Tag{
			Key:   aws.String(k),
			Value: aws.String(v),
		}
		tags = append(tags, tag)
	}
	input := &iam.TagRoleInput{
		RoleName: aws.String(req.Name),
		Tags:     tags,
	}
	_, err := i.Client.TagRole(input)
	if err != nil {
		if aerr, ok := err.(awserr.Error); ok {
			switch aerr.Code() {
			case iam.ErrCodeNoSuchEntityException:
				log.Error(err, iam.ErrCodeNoSuchEntityException)
			case iam.ErrCodeLimitExceededException:
				log.Error(err, iam.ErrCodeLimitExceededException)
			case iam.ErrCodeInvalidInputException:
				log.Error(err, iam.ErrCodeInvalidInputException)
			case iam.ErrCodeServiceFailureException:
				log.Error(err, iam.ErrCodeServiceFailureException)
			default:
				log.Error(err, aerr.Error())
			}
		} else {
			log.Error(err, err.Error())
		}
		return &IAMRoleResponse{}, err
	}
	log.V(1).Info("Successfully completed TagRole call")
	return &IAMRoleResponse{}, nil
}
func (i *IAM) AddPermissionBoundary(ctx context.Context, req IAMRoleRequest) error {
	log := log.Logger(ctx, "awsapi", "iam", "AddPermissionBoundary")
	log = log.WithValues("roleName", req.Name)
	log.V(1).Info("Initiating api call")
	input := &iam.PutRolePermissionsBoundaryInput{
		RoleName:            aws.String(req.Name),
		PermissionsBoundary: aws.String(req.ManagedPermissionBoundaryPolicy),
	}
	if err := input.Validate(); err != nil {
		log.Error(err, "input validation failed")
		return err
	}
	_, err := i.Client.PutRolePermissionsBoundary(input)
	if err != nil {
		if aerr, ok := err.(awserr.Error); ok {
			switch aerr.Code() {
			case iam.ErrCodeNoSuchEntityException:
				log.Error(err, iam.ErrCodeNoSuchEntityException)
			case iam.ErrCodeLimitExceededException:
				log.Error(err, iam.ErrCodeLimitExceededException)
			case iam.ErrCodeInvalidInputException:
				log.Error(err, iam.ErrCodeInvalidInputException)
			case iam.ErrCodeUnmodifiableEntityException:
				log.Error(err, iam.ErrCodeUnmodifiableEntityException)
			case iam.ErrCodePolicyNotAttachableException:
				log.Error(err, iam.ErrCodePolicyNotAttachableException)
			case iam.ErrCodeServiceFailureException:
				log.Error(err, iam.ErrCodeServiceFailureException)
			default:
				log.Error(err, aerr.Error())
			}
		} else {
			log.Error(err, err.Error())
		}
		return err
	}
	log.V(1).Info("Successfuly added permission boundary")
	return nil
}
func (i *IAM) UpdateRole(ctx context.Context, req IAMRoleRequest) (*IAMRoleResponse, error) {
	log := log.Logger(ctx, "awsapi", "iam", "UpdateRole")
	log = log.WithValues("roleName", req.Name)
	log.V(1).Info("Initiating api call")
	input := &iam.UpdateRoleInput{
		RoleName:           aws.String(req.Name),
		MaxSessionDuration: aws.Int64(req.SessionDuration),
		Description:        aws.String(req.Description),
	}
	if err := input.Validate(); err != nil {
		log.Error(err, "input validation failed")
		return nil, err
	}
	_, err := i.Client.UpdateRole(input)
	if err != nil {
		if aerr, ok := err.(awserr.Error); ok {
			switch aerr.Code() {
			case iam.ErrCodeLimitExceededException:
				log.Error(err, iam.ErrCodeLimitExceededException)
			case iam.ErrCodeNoSuchEntityException:
				log.Error(err, iam.ErrCodeNoSuchEntityException)
			case iam.ErrCodeServiceFailureException:
				log.Error(err, iam.ErrCodeServiceFailureException)
			default:
				log.Error(err, "error in update roles")
			}
		} else {
			log.Error(err, "error in update role")
		}
		return nil, err
	}
	log.V(1).Info("Initiating api call", "api", "UpdateAssumeRolePolicy")
	inputPolicy := &iam.UpdateAssumeRolePolicyInput{
		RoleName:       aws.String(req.Name),
		PolicyDocument: aws.String(req.TrustPolicy),
	}
	if err := inputPolicy.Validate(); err != nil {
		log.Error(err, "input validation failed")
		return nil, err
	}
	_, err = i.Client.UpdateAssumeRolePolicy(inputPolicy)
	if err != nil {
		if aerr, ok := err.(awserr.Error); ok {
			switch aerr.Code() {
			case iam.ErrCodeLimitExceededException:
				log.Error(err, iam.ErrCodeLimitExceededException)
			case iam.ErrCodeNoSuchEntityException:
				log.Error(err, iam.ErrCodeNoSuchEntityException)
			case iam.ErrCodeServiceFailureException:
				log.Error(err, iam.ErrCodeServiceFailureException)
			default:
				log.Error(err, aerr.Error())
			}
		} else {
			log.Error(err, err.Error())
		}
		return nil, err
	}
	log.V(1).Info("AssumeRole Policy is successfully updated")
	return i.AttachInlineRolePolicy(ctx, req)
}
func (i *IAM) AttachInlineRolePolicy(ctx context.Context, req IAMRoleRequest) (*IAMRoleResponse, error) {
	log := log.Logger(ctx, "awsapi", "iam", "AttachInlineRolePolicy")
	log = log.WithValues("roleName", req.Name)
	log.V(1).Info("Initiating api call")
	input := &iam.PutRolePolicyInput{
		RoleName:       aws.String(req.Name),
		PolicyName:     aws.String(req.PolicyName),
		PolicyDocument: aws.String(req.PermissionPolicy),
	}
	if err := input.Validate(); err != nil {
		log.Error(err, "input validation failed")
		return nil, err
	}
	_, err := i.Client.PutRolePolicy(input)
	if err != nil {
		if aerr, ok := err.(awserr.Error); ok {
			switch aerr.Code() {
			case iam.ErrCodeMalformedPolicyDocumentException:
				log.Error(err, iam.ErrCodeMalformedPolicyDocumentException)
			case iam.ErrCodeUnmodifiableEntityException:
				log.Error(err, iam.ErrCodeUnmodifiableEntityException)
			case iam.ErrCodeLimitExceededException:
				log.Error(err, iam.ErrCodeLimitExceededException)
			case iam.ErrCodeNoSuchEntityException:
				log.Error(err, iam.ErrCodeNoSuchEntityException)
			case iam.ErrCodeServiceFailureException:
				log.Error(err, iam.ErrCodeServiceFailureException)
			default:
				log.Error(err, aerr.Error())
			}
		}
		return nil, err
	}
	log.V(1).Info("Successfully completed attaching InlineRolePolicy")
	return &IAMRoleResponse{}, nil
}