func CreateIAMRole(roleName, rolePolicyDocument, path string, dryRun bool) (string, error) {
	role, _ := GetIAMRoleByName(roleName)
	if role.Arn != "" {
		terminal.Notice("IAM Role named [" + roleName + "] already exists, skipping.")
		return role.Arn, nil
	}
	if dryRun {
		terminal.Information("--dry-run flag is set, not making any actual changes!")
	}
	sess := session.Must(session.NewSession())
	svc := iam.New(sess)
	params := &iam.CreateRoleInput{
		AssumeRolePolicyDocument: aws.String(rolePolicyDocument),
		RoleName:                 aws.String(roleName),
	}
	if path != "" {
		params.Path = aws.String(path)
	}
	if !dryRun {
		resp, err := svc.CreateRole(params)
		if err != nil {
			return "", err
		}
		roleName := aws.StringValue(resp.Role.RoleName)
		roleArn := aws.StringValue(resp.Role.Arn)
		terminal.Delta("Created IAM Role named [" + roleName + "] with ARN [" + roleArn + "]")
		return roleArn, err
	}
	terminal.Information("Done!")
	return "", nil
}
CreateIAMRole creates a new IAM Role with the provided name, policyDocument, and optional path
package aws
import (
	"errors"
	"net/url"
	"os"
	"reflect"
	"regexp"
	"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/murdinc/awsm/models"
	"github.com/murdinc/terminal"
	"github.com/olekukonko/tablewriter"
)
type IAMUsers []IAMUser
type IAMUser models.IAMUser
type IAMRoles []IAMRole
type IAMPolicies []IAMPolicy
type IAMRole models.IAMRole
type IAMPolicy models.IAMPolicy
type IAMPolicyDocument models.IAMPolicyDocument
type IAMInstanceProfiles []IAMInstanceProfile
type IAMInstanceProfile models.IAMInstanceProfile
func GetIAMUser(username string) (IAMUser, error) {
	user := new(IAMUser)
	params := &iam.GetUserInput{}
	if username != "" {
		params.SetUserName(username)
	}
	sess := session.Must(session.NewSession())
	svc := iam.New(sess)
	resp, err := svc.GetUser(params)
	if err != nil {
		if awsErr, ok := err.(awserr.Error); ok {
			return *user, errors.New(awsErr.Message())
		}
		return *user, err
	}
	user.Marshal(resp.User)
	return *user, nil
}
func GetIAMUsers(search string) (iamList *IAMUsers, err error) {
	sess := session.Must(session.NewSession())
	svc := iam.New(sess)
	result, err := svc.ListUsers(&iam.ListUsersInput{})  
	if err != nil {
		terminal.ShowErrorMessage("Error gathering IAM Users list", err.Error())
		return &IAMUsers{}, err
	}
	iam := make(IAMUsers, len(result.Users))
	for i, user := range result.Users {
		iam[i].Marshal(user)
	}
	iamList = new(IAMUsers)
	if search != "" {
		term := regexp.MustCompile(search)
	Loop:
		for i, ia := range iam {
			rIam := reflect.ValueOf(ia)
			for k := 0; k < rIam.NumField(); k++ {
				sVal := rIam.Field(k).String()
				if term.MatchString(sVal) {
					*iamList = append(*iamList, iam[i])
					continue Loop
				}
			}
		}
	} else {
		*iamList = append(*iamList, iam[:]...)
	}
	return iamList, nil
}
func GetIAMRole(name string) (IAMRole, error) {
	sess := session.Must(session.NewSession())
	svc := iam.New(sess)
	params := &iam.GetRoleInput{
		RoleName: aws.String(name),
	}
	resp, err := svc.GetRole(params)
	if err != nil {
		return IAMRole{}, err
	}
	role := new(IAMRole)
	role.Marshal(resp.Role)
	return *role, nil
}
func GetIAMRolePolicyNames(roleName string) ([]string, error) {
	sess := session.Must(session.NewSession())
	svc := iam.New(sess)
	params := &iam.ListRolePoliciesInput{
		RoleName: aws.String(roleName),
	}
	resp, err := svc.ListRolePolicies(params)
	if err != nil {
		return []string{}, err
	}
	return aws.StringValueSlice(resp.PolicyNames), nil
}
func GetIAMAttachedRolePolicyARNs(roleName string) ([]string, error) {
	sess := session.Must(session.NewSession())
	svc := iam.New(sess)
	params := &iam.ListAttachedRolePoliciesInput{
		RoleName: aws.String(roleName),
	}
	resp, err := svc.ListAttachedRolePolicies(params)
	if err != nil {
		return []string{}, err
	}
	policyArns := make([]string, len(resp.AttachedPolicies))
	for i, policy := range resp.AttachedPolicies {
		policyArns[i] = aws.StringValue(policy.PolicyArn)
	}
	return policyArns, nil
}
func GetIAMRoles(search string) (iamRoleList *IAMRoles, err error) {
	sess := session.Must(session.NewSession())
	svc := iam.New(sess)
	result, err := svc.ListRoles(&iam.ListRolesInput{})
	if err != nil {
		terminal.ShowErrorMessage("Error gathering IAM Roles list", err.Error())
		return &IAMRoles{}, err
	}
	iam := make(IAMRoles, len(result.Roles))
	for i, role := range result.Roles {
		iam[i].Marshal(role)
	}
	iamRoleList = new(IAMRoles)
	if search != "" {
		term := regexp.MustCompile(search)
	Loop:
		for i, ia := range iam {
			rIam := reflect.ValueOf(ia)
			for k := 0; k < rIam.NumField(); k++ {
				sVal := rIam.Field(k).String()
				if term.MatchString(sVal) {
					*iamRoleList = append(*iamRoleList, iam[i])
					continue Loop
				}
			}
		}
	} else {
		*iamRoleList = append(*iamRoleList, iam[:]...)
	}
	return iamRoleList, nil
}
func GetIAMRoleByName(roleName string) (iamRole IAMRole, err error) {
	iamRoles, err := GetIAMRoles("")
	if err != nil {
		return iamRole, nil
	}
	for _, role := range *iamRoles {
		if role.RoleName == roleName {
			return role, nil
		}
	}
	return iamRole, errors.New("No IAM Role found matching [" + roleName + "]!")
}
func GetIAMPolicyByName(policyName string) (iamPolicy IAMPolicy, err error) {
	iamPolicies, err := GetIAMPolicies("")
	if err != nil {
		return iamPolicy, nil
	}
	for _, policy := range *iamPolicies {
		if policy.PolicyName == policyName {
			return policy, nil
		}
	}
	return iamPolicy, errors.New("No IAM Policy found matching [" + policyName + "]!")
}
func GetIAMInstanceProfileByName(instanceProfileName string) (iamInstanceProfile IAMInstanceProfile, err error) {
	iamInstanceProfiles, err := GetIAMInstanceProfiles("")
	if err != nil {
		return iamInstanceProfile, nil
	}
	for _, profile := range *iamInstanceProfiles {
		if profile.ProfileName == instanceProfileName {
			return profile, nil
		}
	}
	return iamInstanceProfile, errors.New("No IAM Instance Profile found matching [" + instanceProfileName + "]!")
}
func GetIAMPolicyDocument(search string, version string) (IAMPolicyDocument, error) {
	policies, err := GetIAMPolicies(search)
	if err != nil {
		return IAMPolicyDocument{}, err
	}
	policyCount := len(*policies)
	if policyCount == 0 {
		return IAMPolicyDocument{}, errors.New("No IAM Policies found for the search term provided")
	} else if policyCount > 1 {
		policies.PrintTable()
		return IAMPolicyDocument{}, errors.New("Please limit your search term to just one Policy")
	}
	policy := (*policies)[0]
	if version == "" {
		version = policy.DefaultVersionId
	}
	sess := session.Must(session.NewSession())
	svc := iam.New(sess)
	params := &iam.GetPolicyVersionInput{
		PolicyArn: aws.String(policy.Arn),
		VersionId: aws.String(version),
	}
	resp, err := svc.GetPolicyVersion(params)
	if err != nil {
		return IAMPolicyDocument{}, err
	}
	policyDocument := new(IAMPolicyDocument)
	policyDocument.Marshal(resp.PolicyVersion)
	return *policyDocument, nil
}
func GetIAMPolicyByARN(policyARN string) (iamPolicy *IAMPolicy, err error) {
	sess := session.Must(session.NewSession())
	svc := iam.New(sess)
	params := &iam.GetPolicyInput{
		PolicyArn: aws.String(policyARN),
	}
	result, err := svc.GetPolicy(params)
	if err != nil {
		terminal.ShowErrorMessage("Error fetching IAM Policy", err.Error())
		return &IAMPolicy{}, err
	}
	iamPolicy = new(IAMPolicy)
	iamPolicy.Marshal(result.Policy)
	return iamPolicy, nil
}
func GetIAMPolicies(search string) (iamPolicyList *IAMPolicies, err error) {
	sess := session.Must(session.NewSession())
	svc := iam.New(sess)
	result, err := svc.ListPolicies(&iam.ListPoliciesInput{})
	if err != nil {
		terminal.ShowErrorMessage("Error gathering IAM Policy list", err.Error())
		return &IAMPolicies{}, err
	}
	iam := make(IAMPolicies, len(result.Policies))
	for i, policy := range result.Policies {
		iam[i].Marshal(policy)
	}
	iamPolicyList = new(IAMPolicies)
	if search != "" {
		term := regexp.MustCompile(search)
	Loop:
		for i, ia := range iam {
			rIam := reflect.ValueOf(ia)
			for k := 0; k < rIam.NumField(); k++ {
				sVal := rIam.Field(k).String()
				if term.MatchString(sVal) {
					*iamPolicyList = append(*iamPolicyList, iam[i])
					continue Loop
				}
			}
		}
	} else {
		*iamPolicyList = append(*iamPolicyList, iam[:]...)
	}
	return iamPolicyList, nil
}
func GetIAMInstanceProfile(name string) (IAMInstanceProfile, error) {
	sess := session.Must(session.NewSession())
	svc := iam.New(sess)
	params := &iam.GetInstanceProfileInput{
		InstanceProfileName: aws.String(name),
	}
	resp, err := svc.GetInstanceProfile(params)
	if err != nil {
		return IAMInstanceProfile{}, err
	}
	profile := new(IAMInstanceProfile)
	profile.Marshal(resp.InstanceProfile)
	return *profile, nil
}
func GetIAMInstanceProfiles(search string) (iamProfileList *IAMInstanceProfiles, err error) {
	sess := session.Must(session.NewSession())
	svc := iam.New(sess)
	result, err := svc.ListInstanceProfiles(&iam.ListInstanceProfilesInput{})
	if err != nil {
		terminal.ShowErrorMessage("Error gathering IAM Instance Profile list", err.Error())
		return &IAMInstanceProfiles{}, err
	}
	iam := make(IAMInstanceProfiles, len(result.InstanceProfiles))
	for i, profile := range result.InstanceProfiles {
		iam[i].Marshal(profile)
	}
	iamProfileList = new(IAMInstanceProfiles)
	if search != "" {
		term := regexp.MustCompile(search)
	Loop:
		for i, ia := range iam {
			rIam := reflect.ValueOf(ia)
			for k := 0; k < rIam.NumField(); k++ {
				sVal := rIam.Field(k).String()
				if term.MatchString(sVal) {
					*iamProfileList = append(*iamProfileList, iam[i])
					continue Loop
				}
			}
		}
	} else {
		*iamProfileList = append(*iamProfileList, iam[:]...)
	}
	return iamProfileList, nil
}
func GetIAMInstanceProfilesForRole(roleName string) (iamInstanceProfileList IAMInstanceProfiles, err error) {
	sess := session.Must(session.NewSession())
	svc := iam.New(sess)
	params := &iam.ListInstanceProfilesForRoleInput{
		RoleName: aws.String(roleName),
	}
	result, err := svc.ListInstanceProfilesForRole(params)
	if err != nil {
		terminal.ShowErrorMessage("Error gathering IAM Instance Profiles list for Role ["+roleName+"]", err.Error())
		return IAMInstanceProfiles{}, err
	}
	iamInstanceProfileList = make(IAMInstanceProfiles, len(result.InstanceProfiles))
	for i, profile := range result.InstanceProfiles {
		iamInstanceProfileList[i].Marshal(profile)
	}
	return iamInstanceProfileList, nil
}
func RemoveIAMRoleFromInstanceProfile(roleName, instanceProfileName string) error {
	sess := session.Must(session.NewSession())
	svc := iam.New(sess)
	params := &iam.RemoveRoleFromInstanceProfileInput{
		InstanceProfileName: aws.String(instanceProfileName),
		RoleName:            aws.String(roleName),
	}
	_, err := svc.RemoveRoleFromInstanceProfile(params)
	if err != nil {
		terminal.ShowErrorMessage("Error removing IAM Role ["+roleName+"] from Instance Profile ["+instanceProfileName+"]", err.Error())
		return err
	}
	terminal.Delta("Removed IAM Role [" + roleName + "] from Instance Profile [" + instanceProfileName + "]!")
	return nil
}
func DetachIAMRolePolicy(roleName, policyArn string) error {
	sess := session.Must(session.NewSession())
	svc := iam.New(sess)
	params := &iam.DetachRolePolicyInput{
		RoleName:  aws.String(roleName),
		PolicyArn: aws.String(policyArn),
	}
	_, err := svc.DetachRolePolicy(params)
	if err != nil {
		terminal.ShowErrorMessage("Error detaching IAM Policy ["+policyArn+"] from Role ["+roleName+"]", err.Error())
		return err
	}
	terminal.Delta("Detached IAM Role [" + roleName + "] from Policy [" + policyArn + "]!")
	return nil
}
func AttachIAMRolePolicy(roleName, policy string, dryRun bool) error {
	if dryRun {
		terminal.Information("--dry-run flag is set, not making any actual changes!")
	}
	roleList, err := GetIAMRoles(roleName)
	if err != nil {
		return err
	}
	roleCount := len(*roleList)
	if roleCount < 1 {
		return errors.New("No IAM Roles found matching [" + roleName + "]")
	} else if roleCount > 1 {
		roleList.PrintTable()
		return errors.New("Please limit your search term to just one IAM Role")
	}
	policies, err := GetIAMPolicies(policy)
	if err != nil {
		return err
	}
	if len(*policies) < 1 {
		return errors.New("No IAM Policies found matching [" + policy + "]")
	}
	terminal.Notice("Role:")
	roleList.PrintTable()
	terminal.Notice("Policies:")
	policies.PrintTable()
	if !terminal.PromptBool("Are you sure you want to attach these policies to this IAM Role?") {
		terminal.ErrorLine("Aborting!")
		return nil
	}
	if !dryRun {
		for _, policy := range *policies {
			err := AttachIAMRolePolicyByARN(roleName, policy.Arn, dryRun)
			if err != nil {
				return err
			}
		}
	}
	terminal.Information("Done!")
	return nil
}
func AttachIAMRolePolicyByARN(roleName, policyARN string, dryRun bool) error {
	attachedPolicyARNs, err := GetIAMAttachedRolePolicyARNs(roleName)
	if err != nil {
		terminal.ShowErrorMessage("Error attaching IAM Policy ["+policyARN+"] to Role ["+roleName+"]", err.Error())
		return err
	}
	for _, attachedPolicyARN := range attachedPolicyARNs {
		if attachedPolicyARN == policyARN {
			terminal.Notice("IAM Policy [" + policyARN + "] is already attached to to Role [" + roleName + "], skipping")
			return nil
		}
	}
	if !dryRun {
		sess := session.Must(session.NewSession())
		svc := iam.New(sess)
		params := &iam.AttachRolePolicyInput{
			RoleName:  aws.String(roleName),
			PolicyArn: aws.String(policyARN),
		}
		_, err = svc.AttachRolePolicy(params)
		if err != nil {
			terminal.ShowErrorMessage("Error attaching IAM Policy ["+policyARN+"] to Role ["+roleName+"]", err.Error())
			return err
		}
		terminal.Delta("Attached IAM Policy [" + policyARN + "] to Role [" + roleName + "]!")
	}
	terminal.Information("Done!")
	return nil
}
func AddIAMRoleToInstanceProfile(roleName, instanceProfileName string, dryRun bool) error {
	instProfiles, err := GetIAMInstanceProfilesForRole(roleName)
	if err != nil {
		terminal.ShowErrorMessage("Error adding IAM Role ["+roleName+"] to Instance Profile ["+instanceProfileName+"]", err.Error())
		return err
	}
	for _, instProfile := range instProfiles {
		if instProfile.ProfileName == instanceProfileName {
			terminal.Notice("IAM Role [" + roleName + "] has already been added to to Instance Profile [" + instanceProfileName + "], skipping.")
			return nil
		}
	}
	if dryRun {
		terminal.Information("--dry-run flag is set, not making any actual changes!")
	}
	if !dryRun {
		sess := session.Must(session.NewSession())
		svc := iam.New(sess)
		params := &iam.AddRoleToInstanceProfileInput{
			InstanceProfileName: aws.String(instanceProfileName),
			RoleName:            aws.String(roleName),
		}
		_, err := svc.AddRoleToInstanceProfile(params)
		if err != nil {
			terminal.ShowErrorMessage("Error adding IAM Role ["+roleName+"] to Instance Profile ["+instanceProfileName+"]", err.Error())
			return err
		}
		terminal.Delta("Attached IAM Role [" + roleName + "] to Instance Profile [" + instanceProfileName + "]!")
	}
	return nil
}
func (i *IAMUser) Marshal(user *iam.User) {
	i.UserName = aws.StringValue(user.UserName)
	i.UserID = aws.StringValue(user.UserId)
	i.CreateDate = aws.TimeValue(user.CreateDate)
	i.Arn = aws.StringValue(user.Arn)
	i.PasswordLastUsed = aws.TimeValue(user.PasswordLastUsed)
}
func (i *IAMRole) Marshal(user *iam.Role) {
	doc, _ := url.Parse(aws.StringValue(user.AssumeRolePolicyDocument))
	docStr := doc.Path
	i.RoleName = aws.StringValue(user.RoleName)
	i.RoleID = aws.StringValue(user.RoleId)
	i.CreateDate = aws.TimeValue(user.CreateDate)
	i.Arn = aws.StringValue(user.Arn)
	i.AssumeRolePolicyDocument = docStr
}
func (i *IAMPolicy) Marshal(policy *iam.Policy) {
	i.PolicyName = aws.StringValue(policy.PolicyName)
	i.PolicyID = aws.StringValue(policy.PolicyId)
	i.Description = aws.StringValue(policy.Description)
	i.IsAttachable = aws.BoolValue(policy.IsAttachable)
	i.CreateDate = aws.TimeValue(policy.CreateDate)
	i.AttachmentCount = int(aws.Int64Value(policy.AttachmentCount))
	i.DefaultVersionId = aws.StringValue(policy.DefaultVersionId)
	i.Arn = aws.StringValue(policy.Arn)
}
func (i *IAMPolicyDocument) Marshal(policyVersion *iam.PolicyVersion) {
	doc, _ := url.Parse(aws.StringValue(policyVersion.Document))
	docStr := doc.Path
	i.Document = docStr
	i.CreateDate = aws.TimeValue(policyVersion.CreateDate)
	i.IsDefaultVersion = aws.BoolValue(policyVersion.IsDefaultVersion)
	i.VersionId = aws.StringValue(policyVersion.VersionId)
}
func (i *IAMInstanceProfile) Marshal(profile *iam.InstanceProfile) {
	i.ProfileName = aws.StringValue(profile.InstanceProfileName)
	i.ProfileID = aws.StringValue(profile.InstanceProfileId)
	i.CreateDate = aws.TimeValue(profile.CreateDate)
	i.Arn = aws.StringValue(profile.Arn)
}
func (i *IAMUsers) PrintTable() {
	if len(*i) == 0 {
		terminal.ShowErrorMessage("Warning", "No IAM Users Found!")
		return
	}
	var header []string
	rows := make([][]string, len(*i))
	for index, user := range *i {
		models.ExtractAwsmTable(index, user, &header, &rows)
	}
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader(header)
	table.AppendBulk(rows)
	table.Render()
}
func (i *IAMRoles) PrintTable() {
	if len(*i) == 0 {
		terminal.ShowErrorMessage("Warning", "No IAM Roles Found!")
		return
	}
	var header []string
	rows := make([][]string, len(*i))
	for index, user := range *i {
		models.ExtractAwsmTable(index, user, &header, &rows)
	}
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader(header)
	table.AppendBulk(rows)
	table.Render()
}
func (i *IAMPolicies) PrintTable() {
	if len(*i) == 0 {
		terminal.ShowErrorMessage("Warning", "No IAM Policies Found!")
		return
	}
	var header []string
	rows := make([][]string, len(*i))
	for index, user := range *i {
		models.ExtractAwsmTable(index, user, &header, &rows)
	}
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader(header)
	table.AppendBulk(rows)
	table.Render()
}
func (i *IAMInstanceProfiles) PrintTable() {
	if len(*i) == 0 {
		terminal.ShowErrorMessage("Warning", "No IAM Instance Profiles Found!")
		return
	}
	var header []string
	rows := make([][]string, len(*i))
	for index, user := range *i {
		models.ExtractAwsmTable(index, user, &header, &rows)
	}
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader(header)
	table.AppendBulk(rows)
	table.Render()
}
func CreateIAMUser(username, path string) error {
	sess := session.Must(session.NewSession())
	svc := iam.New(sess)
	params := &iam.CreateUserInput{
		UserName: aws.String(username),
	}
	if path != "" {
		params.Path = aws.String(path)
	}
	resp, err := svc.CreateUser(params)
	userName := aws.StringValue(resp.User.UserName)
	userId := aws.StringValue(resp.User.UserId)
	terminal.Delta("Created IAM User [" + userName + "] with User ID [" + userId + "]")
	if err == nil {
		terminal.Information("Done!")
	}
	return err
}
func CreateIAMPolicy(policyName, policyDocument, path, description string, dryRun bool) (string, error) {
	policy, _ := GetIAMPolicyByName(policyName)
	if policy.Arn != "" {
		terminal.Notice("IAM Policy named [" + policyName + "] already exists, skipping.")
		return policy.Arn, nil
	}
	if dryRun {
		terminal.Information("--dry-run flag is set, not making any actual changes!")
	}
	sess := session.Must(session.NewSession())
	svc := iam.New(sess)
	params := &iam.CreatePolicyInput{
		PolicyName:     aws.String(policyName),
		PolicyDocument: aws.String(policyDocument),
	}
	if path != "" {
		params.Path = aws.String(path)
	}
	if description != "" {
		params.Description = aws.String(description)
	}
	if !dryRun {
		resp, err := svc.CreatePolicy(params)
		if err != nil {
			return "", err
		}
		policyName := aws.StringValue(resp.Policy.PolicyName)
		policyArn := aws.StringValue(resp.Policy.Arn)
		terminal.Delta("Created IAM Policy named [" + policyName + "] with ARN [" + policyArn + "]")
		return policyArn, err
	}
	terminal.Information("Done!")
	return "", nil
}