Skip to content

Commit 04a35df

Browse files
Merge pull request #28 from Santiago-Labs/ethan/scp-support
Assign SCP Stacks to OUs/Accounts
2 parents 320d36b + 61f71df commit 04a35df

File tree

22 files changed

+388
-165
lines changed

22 files changed

+388
-165
lines changed

cmd/deploy.go

+2-31
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
11
package cmd
22

33
import (
4-
"context"
5-
"fmt"
6-
74
"github.com/santiago-labs/telophasecli/cmd/runner"
8-
"github.com/santiago-labs/telophasecli/lib/awsorgs"
9-
"github.com/santiago-labs/telophasecli/lib/ymlparser"
10-
"github.com/santiago-labs/telophasecli/resource"
115
"github.com/santiago-labs/telophasecli/resourceoperation"
126

137
"github.com/spf13/cobra"
@@ -33,8 +27,6 @@ var compileCmd = &cobra.Command{
3327
Use: "deploy",
3428
Short: "deploy - Deploy a CDK and/or TF stacks to your AWS account(s). Accounts in organization.yml will be created if they do not exist.",
3529
Run: func(cmd *cobra.Command, args []string) {
36-
orgClient := awsorgs.New()
37-
ctx := context.Background()
3830

3931
var consoleUI runner.ConsoleUI
4032
if useTUI {
@@ -43,29 +35,8 @@ var compileCmd = &cobra.Command{
4335
consoleUI = runner.NewSTDOut()
4436
}
4537

46-
var accountsToApply []resource.Account
47-
rootAWSGroup, err := ymlparser.ParseOrganizationV2(orgFile)
48-
if err != nil {
49-
panic(fmt.Sprintf("error: %s", err))
50-
}
51-
52-
ops := orgV2Diff(ctx, consoleUI, orgClient, rootAWSGroup, resourceoperation.Deploy)
53-
for _, op := range ops {
54-
op.Call(ctx)
55-
}
56-
57-
if rootAWSGroup != nil {
58-
for _, acct := range rootAWSGroup.AllDescendentAccounts() {
59-
if contains(tag, acct.AllTags()) || tag == "" {
60-
accountsToApply = append(accountsToApply, *acct)
61-
}
62-
}
63-
}
64-
65-
if len(accountsToApply) == 0 {
66-
fmt.Println("No accounts to deploy")
67-
}
38+
go processOrgEndToEnd(consoleUI, resourceoperation.Deploy)
39+
consoleUI.Start()
6840

69-
runIAC(ctx, consoleUI, resourceoperation.Deploy, accountsToApply)
7041
},
7142
}

cmd/diff.go

+2-30
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
11
package cmd
22

33
import (
4-
"context"
5-
"fmt"
6-
74
"github.com/santiago-labs/telophasecli/cmd/runner"
8-
"github.com/santiago-labs/telophasecli/lib/awsorgs"
9-
"github.com/santiago-labs/telophasecli/lib/ymlparser"
10-
"github.com/santiago-labs/telophasecli/resource"
115
"github.com/santiago-labs/telophasecli/resourceoperation"
126

137
"github.com/spf13/cobra"
@@ -25,7 +19,6 @@ var diffCmd = &cobra.Command{
2519
Use: "diff",
2620
Short: "diff - Show accounts to create/update and CDK and/or TF changes for each account.",
2721
Run: func(cmd *cobra.Command, args []string) {
28-
orgClient := awsorgs.New()
2922

3023
var consoleUI runner.ConsoleUI
3124
if useTUI {
@@ -34,28 +27,7 @@ var diffCmd = &cobra.Command{
3427
consoleUI = runner.NewSTDOut()
3528
}
3629

37-
var accountsToApply []resource.Account
38-
39-
ctx := context.Background()
40-
41-
rootAWSGroup, err := ymlparser.ParseOrganizationV2(orgFile)
42-
if err != nil {
43-
panic(fmt.Sprintf("error: %s", err))
44-
}
45-
orgV2Diff(ctx, consoleUI, orgClient, rootAWSGroup, resourceoperation.Diff)
46-
47-
if rootAWSGroup != nil {
48-
for _, acct := range rootAWSGroup.AllDescendentAccounts() {
49-
if contains(tag, acct.AllTags()) || tag == "" {
50-
accountsToApply = append(accountsToApply, *acct)
51-
}
52-
}
53-
}
54-
55-
if len(accountsToApply) == 0 {
56-
fmt.Println("No accounts to deploy")
57-
}
58-
59-
runIAC(ctx, consoleUI, resourceoperation.Diff, accountsToApply)
30+
go processOrgEndToEnd(consoleUI, resourceoperation.Diff)
31+
consoleUI.Start()
6032
},
6133
}

cmd/iac.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,13 @@ func runIAC(
3535
}
3636

3737
for _, op := range ops {
38-
op.Call(ctx)
38+
if err := op.Call(ctx); err != nil {
39+
panic(err)
40+
}
3941
}
4042
}(accts[i])
4143
}
4244

43-
consoleUI.PostProcess()
4445
wg.Wait()
4546
}
4647
func contains(e string, s []string) bool {

cmd/provisionaccounts.go

+42-46
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,10 @@ import (
55
"errors"
66
"fmt"
77

8-
"github.com/aws/aws-sdk-go/aws/session"
9-
10-
"github.com/aws/aws-sdk-go/service/sts"
118
"github.com/spf13/cobra"
129

1310
"github.com/santiago-labs/telophasecli/cmd/runner"
1411
"github.com/santiago-labs/telophasecli/lib/awsorgs"
15-
"github.com/santiago-labs/telophasecli/lib/awssess"
1612
"github.com/santiago-labs/telophasecli/lib/ymlparser"
1713
"github.com/santiago-labs/telophasecli/resource"
1814
"github.com/santiago-labs/telophasecli/resourceoperation"
@@ -23,6 +19,7 @@ var orgFile string
2319
func init() {
2420
rootCmd.AddCommand(accountProvision)
2521
accountProvision.Flags().StringVar(&orgFile, "org", "organization.yml", "Path to the organization.yml file")
22+
accountProvision.Flags().BoolVar(&useTUI, "tui", false, "use the TUI for diff")
2623
}
2724

2825
func isValidAccountArg(arg string) bool {
@@ -51,7 +48,6 @@ var accountProvision = &cobra.Command{
5148
return fmt.Errorf("invalid color specified: %s", args[0])
5249
},
5350
Run: func(cmd *cobra.Command, args []string) {
54-
orgClient := awsorgs.New()
5551

5652
var consoleUI runner.ConsoleUI
5753
if useTUI {
@@ -60,48 +56,61 @@ var accountProvision = &cobra.Command{
6056
consoleUI = runner.NewSTDOut()
6157
}
6258

63-
ctx := context.Background()
64-
if args[0] == "import" {
65-
if err := importOrgV2(orgClient); err != nil {
66-
panic(fmt.Sprintf("error importing organization: %s", err))
67-
}
68-
}
59+
go processOrg(consoleUI, args[0])
60+
consoleUI.Start()
6961

70-
rootAWSGroup, err := ymlparser.ParseOrganizationV2(orgFile)
71-
if err != nil {
72-
panic(fmt.Sprintf("error: %s", err))
73-
}
74-
if args[0] == "diff" {
75-
orgV2Diff(ctx, consoleUI, orgClient, rootAWSGroup, resourceoperation.Diff)
62+
},
63+
}
64+
65+
func processOrg(consoleUI runner.ConsoleUI, cmd string) {
66+
orgClient := awsorgs.New()
67+
ctx := context.Background()
68+
mgmtAcct, err := orgClient.FetchManagementAccount(ctx)
69+
if err != nil {
70+
panic(err)
71+
}
72+
if cmd == "import" {
73+
consoleUI.Print("Importing AWS Organization", *mgmtAcct)
74+
if err := importOrgV2(ctx, consoleUI, orgClient, mgmtAcct); err != nil {
75+
consoleUI.Print(fmt.Sprintf("error importing organization: %s", err), *mgmtAcct)
7676
}
77+
}
78+
79+
rootAWSGroup, err := ymlparser.ParseOrganizationV2(orgFile)
80+
if err != nil {
81+
consoleUI.Print(fmt.Sprintf("error parsing organization: %s", err), *mgmtAcct)
82+
}
83+
if cmd == "diff" {
84+
consoleUI.Print("Diffing AWS Organization", *mgmtAcct)
85+
orgV2Diff(ctx, consoleUI, orgClient, rootAWSGroup, mgmtAcct, resourceoperation.Diff)
86+
}
7787

78-
if args[0] == "deploy" {
79-
operations := orgV2Diff(ctx, consoleUI, orgClient, rootAWSGroup, resourceoperation.Deploy)
88+
if cmd == "deploy" {
89+
consoleUI.Print("Diffing AWS Organization", *mgmtAcct)
90+
operations := orgV2Diff(ctx, consoleUI, orgClient, rootAWSGroup, mgmtAcct, resourceoperation.Deploy)
8091

81-
for _, op := range operations {
82-
err := op.Call(ctx)
83-
if err != nil {
84-
panic(fmt.Sprintf("error: %s", err))
85-
}
92+
for _, op := range operations {
93+
err := op.Call(ctx)
94+
if err != nil {
95+
panic(fmt.Sprintf("error: %s", err))
8696
}
8797
}
88-
},
98+
}
99+
100+
consoleUI.Print("Done.", *mgmtAcct)
89101
}
90102

91103
func orgV2Diff(
92104
ctx context.Context,
93105
outputUI runner.ConsoleUI,
94106
orgClient awsorgs.Client,
95107
rootAWSGroup *resource.AccountGroup,
108+
mgmtAcct *resource.Account,
96109
operation int,
97110
) []resourceoperation.ResourceOperation {
98111

99112
var operations []resourceoperation.ResourceOperation
100113
if rootAWSGroup != nil {
101-
mgmtAcct, err := orgClient.FetchManagementAccount(ctx)
102-
if err != nil {
103-
panic(err)
104-
}
105114
operations = append(operations, resourceoperation.CollectOrganizationUnitOps(
106115
ctx, outputUI, orgClient, rootAWSGroup, operation,
107116
)...)
@@ -116,21 +125,7 @@ func orgV2Diff(
116125
return operations
117126
}
118127

119-
func currentAccountID() (string, error) {
120-
stsClient := sts.New(session.Must(awssess.DefaultSession()))
121-
caller, err := stsClient.GetCallerIdentity(&sts.GetCallerIdentityInput{})
122-
if err != nil {
123-
return "", err
124-
}
125-
126-
return *caller.Account, nil
127-
}
128-
129-
func importOrgV2(orgClient awsorgs.Client) error {
130-
managingAccountID, err := currentAccountID()
131-
if err != nil {
132-
return err
133-
}
128+
func importOrgV2(ctx context.Context, consoleUI runner.ConsoleUI, orgClient awsorgs.Client, mgmtAcct *resource.Account) error {
134129

135130
rootId, err := orgClient.GetRootId()
136131
if err != nil {
@@ -140,12 +135,12 @@ func importOrgV2(orgClient awsorgs.Client) error {
140135
return fmt.Errorf("no root ID found")
141136
}
142137

143-
rootGroup, err := orgClient.FetchGroupAndDescendents(context.TODO(), rootId, managingAccountID)
138+
rootGroup, err := orgClient.FetchGroupAndDescendents(ctx, rootId, mgmtAcct.AccountID)
144139
if err != nil {
145140
return err
146141
}
147142
org := resource.AccountGroup{
148-
Name: rootGroup.Name,
143+
GroupName: rootGroup.GroupName,
149144
ChildGroups: rootGroup.ChildGroups,
150145
Accounts: rootGroup.Accounts,
151146
}
@@ -154,5 +149,6 @@ func importOrgV2(orgClient awsorgs.Client) error {
154149
return err
155150
}
156151

152+
consoleUI.Print(fmt.Sprintf("Successfully wrote file to: %s", orgFile), *mgmtAcct)
157153
return nil
158154
}

cmd/root.go

+48
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
package cmd
22

33
import (
4+
"context"
45
"fmt"
56
"os"
67

8+
"github.com/santiago-labs/telophasecli/cmd/runner"
9+
"github.com/santiago-labs/telophasecli/lib/awsorgs"
710
"github.com/santiago-labs/telophasecli/lib/metrics"
11+
"github.com/santiago-labs/telophasecli/lib/ymlparser"
12+
"github.com/santiago-labs/telophasecli/resource"
13+
"github.com/santiago-labs/telophasecli/resourceoperation"
814
"github.com/spf13/cobra"
915
)
1016

@@ -27,3 +33,45 @@ func Execute() {
2733
os.Exit(1)
2834
}
2935
}
36+
37+
func processOrgEndToEnd(consoleUI runner.ConsoleUI, cmd int) {
38+
ctx := context.Background()
39+
orgClient := awsorgs.New()
40+
mgmtAcct, err := orgClient.FetchManagementAccount(ctx)
41+
if err != nil {
42+
panic(err)
43+
}
44+
45+
var accountsToApply []resource.Account
46+
rootAWSGroup, err := ymlparser.ParseOrganizationV2(orgFile)
47+
if err != nil {
48+
panic(fmt.Sprintf("error: %s", err))
49+
}
50+
orgV2Diff(ctx, consoleUI, orgClient, rootAWSGroup, mgmtAcct, cmd)
51+
52+
if rootAWSGroup != nil {
53+
for _, acct := range rootAWSGroup.AllDescendentAccounts() {
54+
if contains(tag, acct.AllTags()) || tag == "" {
55+
accountsToApply = append(accountsToApply, *acct)
56+
}
57+
}
58+
}
59+
60+
if len(accountsToApply) == 0 {
61+
consoleUI.Print("No accounts to deploy.", *mgmtAcct)
62+
}
63+
64+
runIAC(ctx, consoleUI, cmd, accountsToApply)
65+
66+
scpOps := resourceoperation.CollectSCPOps(ctx, orgClient, consoleUI, cmd, rootAWSGroup, mgmtAcct)
67+
for _, op := range scpOps {
68+
err := op.Call(ctx)
69+
if err != nil {
70+
consoleUI.Print(fmt.Sprintf("Error on SCP Operation: %v", err), *mgmtAcct)
71+
}
72+
}
73+
74+
if len(scpOps) == 0 {
75+
consoleUI.Print("No Service Control Policies to deploy.", *mgmtAcct)
76+
}
77+
}

cmd/runner/interface.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ import (
99
type ConsoleUI interface {
1010
Print(string, resource.Account)
1111
RunCmd(*exec.Cmd, resource.Account) error
12-
PostProcess()
12+
Start()
1313
}

cmd/runner/stdout.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func (s *stdOut) RunCmd(cmd *exec.Cmd, acct resource.Account) error {
6060

6161
var scannerWg sync.WaitGroup
6262
scannerWg.Add(2)
63-
scanF := func(scanner *bufio.Scanner, name string) {
63+
scanF := func(scanner *bufio.Scanner, _ string) {
6464
defer scannerWg.Done()
6565
for scanner.Scan() {
6666
fmt.Printf("%s %s\n", s.ColoredId(acct), scanner.Text())
@@ -86,4 +86,4 @@ func (s *stdOut) Print(msg string, acct resource.Account) {
8686
fmt.Printf("%s %v\n", s.ColoredId(acct), msg)
8787
}
8888

89-
func (s *stdOut) PostProcess() {}
89+
func (s *stdOut) Start() {}

0 commit comments

Comments
 (0)