Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Manage IAM instance profiles #39

Merged
merged 7 commits into from
Nov 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion iamy.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
var (
Version string = "dev"
defaultDir string
dryRun *bool
dryRun *bool
)

type logWriter struct{ *log.Logger }
Expand Down
37 changes: 35 additions & 2 deletions iamy/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ func (a *AwsFetcher) fetchS3Data() error {
}
func (a *AwsFetcher) fetchIamData() error {
var populateIamDataErr error
var populateInstanceProfileErr error
err := a.iam.GetAccountAuthorizationDetailsPages(
&iam.GetAccountAuthorizationDetailsInput{
Filter: aws.StringSlice([]string{
Expand All @@ -119,7 +120,6 @@ func (a *AwsFetcher) fetchIamData() error {
if populateIamDataErr != nil {
return false
}

return true
},
)
Expand All @@ -129,7 +129,21 @@ func (a *AwsFetcher) fetchIamData() error {
if err != nil {
return err
}

// Fetch instance profiles
err = a.iam.ListInstanceProfilesPages(&iam.ListInstanceProfilesInput{},
func(resp *iam.ListInstanceProfilesOutput, lastPage bool) bool {
populateInstanceProfileErr = a.populateInstanceProfileData(resp)
if populateInstanceProfileErr != nil {
return false
}
return true
})
if populateInstanceProfileErr != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this need to be here? looks like you've already got a check for this on L136.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's right, the L136 is to escape the iterator function

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, it needs to be there.
The function that includes line 136 is called by the paging function a.iam.ListInstanceProfilesPages with each back results. So the return false is effectively the break.

return err
}
if err != nil {
return err
}
return nil
}

Expand Down Expand Up @@ -176,6 +190,25 @@ func (a *AwsFetcher) marshalRoleDescriptionAsync(roleName string, target *string
}()
}

func (a *AwsFetcher) populateInstanceProfileData(resp *iam.ListInstanceProfilesOutput) error {
for _, profileResp := range resp.InstanceProfiles {
if cfnResourceRegexp.MatchString(*profileResp.InstanceProfileName) {
log.Printf("Skipping CloudFormation generated instance profile %s", *profileResp.InstanceProfileName)
continue
}
profile := InstanceProfile{iamService: iamService{
Name: *profileResp.InstanceProfileName,
Path: *profileResp.Path,
}}
for _, roleResp := range profileResp.Roles {
role := *(roleResp.RoleName)
profile.Roles = append(profile.Roles, role)
}
a.data.InstanceProfiles = append(a.data.InstanceProfiles, &profile)
}
return nil
}

func (a *AwsFetcher) populateIamData(resp *iam.GetAccountAuthorizationDetailsOutput) error {
for _, userResp := range resp.UserDetailList {
if cfnResourceRegexp.MatchString(*userResp.UserName) {
Expand Down
29 changes: 29 additions & 0 deletions iamy/awsdiff.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,12 @@ func (a *awsSyncCmdGenerator) deleteOldEntities() {
"--policy-arn", Arn(fromPolicy, a.to.Account))
}
}
for _, fromInstanceProfile := range a.from.InstanceProfiles {
if found, _ := a.to.FindInstanceProfileByName(fromInstanceProfile.Name, fromInstanceProfile.Path); !found {
a.cmds.Add("aws", "iam", "delete-instance-profile",
"--instance-profile-name", fromInstanceProfile.Name)
}
}
}

func (a *awsSyncCmdGenerator) updatePolicies() {
Expand Down Expand Up @@ -409,6 +415,28 @@ func (a *awsSyncCmdGenerator) updateUsers() {
}
}
}
func (a *awsSyncCmdGenerator) updateInstanceProfiles() {
// update instance profiles
for _, toInstanceProfile := range a.to.InstanceProfiles {
if found, fromInstanceProfile := a.from.FindInstanceProfileByName(toInstanceProfile.Name, toInstanceProfile.Path); found {
// remove old roles from instance profile
for _, role := range stringSetDifference(fromInstanceProfile.Roles, toInstanceProfile.Roles) {
a.cmds.Add("aws", "iam", "remove-role-from-instance-profile", "--instance-profile-name", toInstanceProfile.Name, "--role-name", role)
}

// add new roles to instance profile
for _, role := range stringSetDifference(toInstanceProfile.Roles, fromInstanceProfile.Roles) {
a.cmds.Add("aws", "iam", "add-role-to-instance-profile", "--instance-profile-name", toInstanceProfile.Name, "--role-name", role)
}
} else {
// Create instance profile
a.cmds.Add("aws", "create-instance-profile", "--instance-profile-name", toInstanceProfile.Name, "--path", path(toInstanceProfile.Path))
for _, role := range toInstanceProfile.Roles {
a.cmds.Add("aws", "iam", "add-role-to-instance-profile", "--instance-profile-name", toInstanceProfile.Name, "--role-name", role)
}
}
}
}

func (a *awsSyncCmdGenerator) updateBucketPolicies() {
for _, fromBucketPolicy := range a.from.BucketPolicies {
Expand Down Expand Up @@ -437,6 +465,7 @@ func (a *awsSyncCmdGenerator) GenerateCmds() CmdList {
a.updateRoles()
a.updateGroups()
a.updateUsers()
a.updateInstanceProfiles()
a.updateBucketPolicies()
a.deleteOldEntities()

Expand Down
47 changes: 36 additions & 11 deletions iamy/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,15 @@ type Role struct {
Policies []string `json:"Policies,omitempty"`
}

type InstanceProfile struct {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we probably want to fix the alignment on this one

type InstanceProfile struct {
	iamService `json:"-"`
	Roles      []string `json:"Roles,omitempty"`
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's aligned if your tabs are set to 8 :-)

iamService `json:"-"`
Roles []string `json:"Roles,omitempty"`
}

func (ip InstanceProfile) ResourceType() string {
return "instance-profile"
}

func (r Role) ResourceType() string {
return "role"
}
Expand All @@ -139,21 +148,23 @@ func (bp BucketPolicy) ResourcePath() string {
}

type AccountData struct {
Account *Account
Users []*User
Groups []*Group
Roles []*Role
Policies []*Policy
BucketPolicies []*BucketPolicy
Account *Account
Users []*User
Groups []*Group
Roles []*Role
Policies []*Policy
BucketPolicies []*BucketPolicy
InstanceProfiles []*InstanceProfile
}

func NewAccountData(account string) *AccountData {
return &AccountData{
Account: NewAccountFromString(account),
Users: []*User{},
Groups: []*Group{},
Roles: []*Role{},
Policies: []*Policy{},
Account: NewAccountFromString(account),
Users: []*User{},
Groups: []*Group{},
Roles: []*Role{},
Policies: []*Policy{},
InstanceProfiles: []*InstanceProfile{},
}
}

Expand All @@ -173,6 +184,10 @@ func (a *AccountData) addPolicy(p *Policy) {
a.Policies = append(a.Policies, p)
}

func (a *AccountData) addInstanceProfile(p *InstanceProfile) {
a.InstanceProfiles = append(a.InstanceProfiles, p)
}

func (a *AccountData) addBucketPolicy(bp *BucketPolicy) {
a.BucketPolicies = append(a.BucketPolicies, bp)
}
Expand Down Expand Up @@ -217,6 +232,16 @@ func (a *AccountData) FindPolicyByName(name, path string) (bool, *Policy) {
return false, nil
}

func (a *AccountData) FindInstanceProfileByName(name, path string) (bool, *InstanceProfile) {
for _, p := range a.InstanceProfiles {
if p.Name == name && p.Path == path {
return true, p
}
}

return false, nil
}

func (a *AccountData) FindBucketPolicyByBucketName(name string) (bool, *BucketPolicy) {
for _, p := range a.BucketPolicies {
if p.BucketName == name {
Expand Down
12 changes: 11 additions & 1 deletion iamy/yaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
)

const pathTemplateBlob = "{{.Account}}/{{.Resource.Service}}/{{.Resource.ResourceType}}{{.Resource.ResourcePath}}{{.Resource.ResourceName}}.yaml"
const pathRegexBlob = `^(?P<account>[^/]+)/(?P<entity>(iam/user|iam/group|iam/policy|iam/role|s3))(?P<resourcepath>.*/)(?P<resourcename>[^/]+)\.yaml$`
const pathRegexBlob = `^(?P<account>[^/]+)/(?P<entity>(iam/instance-profile|iam/user|iam/group|iam/policy|iam/role|s3))(?P<resourcepath>.*/)(?P<resourcename>[^/]+)\.yaml$`

var pathTemplate = template.Must(template.New("").Parse(pathTemplateBlob))
var pathRegex = regexp.MustCompile(pathRegexBlob)
Expand Down Expand Up @@ -106,6 +106,10 @@ func (a *YamlLoadDumper) Load() ([]AccountData, error) {
p := Policy{iamService: nameAndPath}
err = a.unmarshalYamlFile(fp, &p)
accounts[accountid].addPolicy(&p)
case "iam/instance-profile":
profile := InstanceProfile{iamService: nameAndPath}
err = a.unmarshalYamlFile(fp, &profile)
accounts[accountid].addInstanceProfile(&profile)
case "s3":
bp := BucketPolicy{BucketName: name}
err = a.unmarshalYamlFile(fp, &bp)
Expand Down Expand Up @@ -168,6 +172,12 @@ func (f *YamlLoadDumper) Dump(accountData *AccountData, canDelete bool) error {
}
}

for _, profile := range accountData.InstanceProfiles {
if err := f.writeResource(accountData.Account, profile); err != nil {
return err
}
}

for _, bucketPolicy := range accountData.BucketPolicies {
if err := f.writeResource(accountData.Account, bucketPolicy); err != nil {
return err
Expand Down