Skip to content

Commit 9ee9041

Browse files
committed
resourceop account: allow delegating services to accounts
This allows someone to delegate different service principals to accounts managed in telophase. One example is: ``` OrganizationUnits: - Name: Security Accounts: - Email: [email protected] AccountName: Audit DelegatedAdministratorServices: - "config.amazonaws.com" - "config-multiaccountsetup.amazonaws.com" ``` This will end up calling `register-delegated-admin` with the listed service principals on the account defined.
1 parent 0e1d824 commit 9ee9041

File tree

6 files changed

+108
-8
lines changed

6 files changed

+108
-8
lines changed

lib/awsorgs/organization.go

+55
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,61 @@ func (c Client) ListAccountsForParent(parentID string) ([]*organizations.Account
434434
return accounts, oops.Wrapf(err, "organizations.ListAccountsForParent")
435435
}
436436

437+
func (c Client) DelegateAdmin(ctx context.Context, acctID, servicePrincipal string) error {
438+
if _, err := c.organizationClient.EnableAWSServiceAccessWithContext(ctx, &organizations.EnableAWSServiceAccessInput{
439+
ServicePrincipal: &servicePrincipal,
440+
}); err != nil {
441+
return oops.Wrapf(err, "organizations.EnableAWSServiceAccess service %s", servicePrincipal)
442+
}
443+
444+
_, err := c.organizationClient.RegisterDelegatedAdministratorWithContext(ctx, &organizations.RegisterDelegatedAdministratorInput{
445+
AccountId: &acctID,
446+
ServicePrincipal: &servicePrincipal,
447+
})
448+
if err != nil {
449+
return oops.Wrapf(err, "organizations.RegisterDelegatedAdministrator service %s", servicePrincipal)
450+
}
451+
452+
return nil
453+
}
454+
455+
// FetchDelegatedAdminPrincipals returns a list of accounts that have delegated
456+
// admin permissions and the service principals with a key of account ID and
457+
// value with a slice of service principals that are delegated to the account key.
458+
func (c Client) FetchDelegatedAdminPrincipals(ctx context.Context) (map[string][]string, error) {
459+
var delegatedAccounts []string
460+
err := c.organizationClient.ListDelegatedAdministratorsPagesWithContext(ctx, &organizations.ListDelegatedAdministratorsInput{},
461+
func(page *organizations.ListDelegatedAdministratorsOutput, lastPage bool) bool {
462+
for _, acct := range page.DelegatedAdministrators {
463+
delegatedAccounts = append(delegatedAccounts, *acct.Id)
464+
}
465+
return !lastPage
466+
})
467+
if err != nil {
468+
return nil, oops.Wrapf(err, "organizations.ListDelegatedAdministrators")
469+
}
470+
471+
// Now we need to see what services are enabled for each account
472+
resp := make(map[string][]string)
473+
for _, acct := range delegatedAccounts {
474+
var servicePrincipals []string
475+
err := c.organizationClient.ListDelegatedServicesForAccountPagesWithContext(ctx, &organizations.ListDelegatedServicesForAccountInput{
476+
AccountId: &acct,
477+
}, func(page *organizations.ListDelegatedServicesForAccountOutput, lastPage bool) bool {
478+
for _, service := range page.DelegatedServices {
479+
servicePrincipals = append(servicePrincipals, *service.ServicePrincipal)
480+
}
481+
return !lastPage
482+
})
483+
if err != nil {
484+
return nil, oops.Wrapf(err, "organizations.ListDelegatedServicesForAccount acctID: %s", acct)
485+
}
486+
resp[acct] = servicePrincipals
487+
}
488+
489+
return resp, nil
490+
}
491+
437492
func (c Client) FetchOUAndDescendents(ctx context.Context, ouID, mgmtAccountID string) (resource.OrganizationUnit, error) {
438493
var ou resource.OrganizationUnit
439494

mintlifydocs/config/organization.mdx

+1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ Accounts:
4949
# If deleting an account you need to pass in --allow-account-delete to telophasecli as a confirmation of the deletion.
5050
Tags: # (Optional) Telophase label for this account. Tags translate to AWS tags with a `=` as the key value delimiter. For example, `telophase:env=prod`
5151
Stacks: # (Optional) Terraform, Cloudformation and CDK stacks to apply to all accounts in this Organization Unit.
52+
DelegatedAdministratorServices: # (Optional) List of delegated service principals for the current account (e.g. config.amazonaws.com)
5253
```
5354
5455
## Example

resource/account.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@ type Account struct {
2222
ServiceControlPolicies []Stack `yaml:"ServiceControlPolicies,omitempty"`
2323
ManagementAccount bool `yaml:"-"`
2424

25-
Delete bool `yaml:"Delete"`
26-
DelegatedAdministrator bool `yaml:"DelegatedAdministrator,omitempty"`
27-
Parent *OrganizationUnit `yaml:"-"`
25+
Delete bool `yaml:"Delete"`
26+
DelegatedAdministrator bool `yaml:"DelegatedAdministrator,omitempty"`
27+
DelegatedAdministratorServices []string `yaml:"DelegatedAdministratorServices,omitempty"`
28+
Parent *OrganizationUnit `yaml:"-"`
2829

2930
Status string `yaml:"-,omitempty"`
3031
}

resourceoperation/account.go

+18
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ type accountOperation struct {
2626
OrgClient *awsorgs.Client
2727
TagsDiff *TagsDiff
2828
AllowDelete bool
29+
30+
DelegateAdminPrincipal string
2931
}
3032

3133
func NewAccountOperation(
@@ -54,6 +56,10 @@ func (ao *accountOperation) SetAllowDelete(allowDelete bool) {
5456
ao.AllowDelete = allowDelete
5557
}
5658

59+
func (ao *accountOperation) SetDelegateAdminPrincipal(principal string) {
60+
ao.DelegateAdminPrincipal = principal
61+
}
62+
5763
func CollectAccountOps(
5864
ctx context.Context,
5965
consoleUI runner.ConsoleUI,
@@ -147,6 +153,11 @@ func (ao *accountOperation) Call(ctx context.Context) error {
147153
if err != nil {
148154
return oops.Wrapf(err, "CloseAccounts")
149155
}
156+
} else if ao.Operation == DelegateAdmin {
157+
err := ao.OrgClient.DelegateAdmin(ctx, ao.Account.AccountID, ao.DelegateAdminPrincipal)
158+
if err != nil {
159+
return oops.Wrapf(err, "DelegateAdmin principal: %s", ao.DelegateAdminPrincipal)
160+
}
150161
}
151162

152163
for _, op := range ao.DependentOperations {
@@ -221,6 +232,13 @@ Tags: `
221232
printColor = "red"
222233
}
223234
}
235+
} else if ao.Operation == DelegateAdmin {
236+
templated = "\n" + `(Delegate Service)
237+
ID: {{ .Account.AccountID }}
238+
Name: {{ .Account.AccountName }}
239+
+ ServicePrincipal: {{ .DelegateAdminPrincipal }}
240+
`
241+
printColor = "green"
224242
}
225243

226244
tpl, err := template.New("operation").Funcs(template.FuncMap{

resourceoperation/interface.go

+6-5
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@ import (
66

77
const (
88
// Accounts
9-
UpdateParent = 1
10-
Create = 2
11-
Update = 3
12-
UpdateTags = 6
13-
Delete = 7
9+
UpdateParent = 1
10+
Create = 2
11+
Update = 3
12+
UpdateTags = 6
13+
Delete = 7
14+
DelegateAdmin = 8
1415

1516
// IaC
1617
Diff = 4

resourceoperation/organization_unit.go

+24
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ func CollectOrganizationUnitOps(
7777
return []ResourceOperation{}
7878
}
7979

80+
delegatedAdmins, err := orgClient.FetchDelegatedAdminPrincipals(ctx)
81+
if err != nil {
82+
consoleUI.Print(fmt.Sprintf("Failed to fetch delegated admins, continuing anyway, error: %v", err), *mgmtAcct)
83+
}
84+
8085
providerOUs := providerRootOU.AllDescendentOUs()
8186
for _, parsedOU := range rootOU.AllDescendentOUs() {
8287
var found bool
@@ -251,6 +256,25 @@ func CollectOrganizationUnitOps(
251256
operations = append(operations, op)
252257
}
253258

259+
if len(parsedAcct.DelegatedAdministratorServices) > 0 && delegatedAdmins != nil {
260+
for _, delegatedAdminService := range parsedAcct.DelegatedAdministratorServices {
261+
if services := delegatedAdmins[parsedAcct.AccountID]; !oneOf(delegatedAdminService, services) {
262+
op := NewAccountOperation(
263+
orgClient,
264+
consoleUI,
265+
parsedAcct,
266+
mgmtAcct,
267+
DelegateAdmin,
268+
nil,
269+
nil,
270+
nil,
271+
)
272+
op.SetDelegateAdminPrincipal(delegatedAdminService)
273+
operations = append(operations, op)
274+
}
275+
}
276+
}
277+
254278
break
255279
}
256280
}

0 commit comments

Comments
 (0)