Skip to content

Commit

Permalink
feat: CloudFormation Service Role support
Browse files Browse the repository at this point in the history
In case your role is too restrictive that `eksctl create cluster` fails due to cloudformation reporting insufficient permissions, specify a service role used by CloudFormation to call AWS API while provisioning stacks on your behalf.

```
eksctl create cluster --cfn-role-arn arn:aws:iam:YOUR_AWS_ACCOUNT_ID:role/eksctl
```

Resolves #329
  • Loading branch information
mumoshu authored and errordeveloper committed Dec 19, 2018
1 parent ff01af9 commit c9a10f0
Show file tree
Hide file tree
Showing 7 changed files with 30 additions and 0 deletions.
12 changes: 12 additions & 0 deletions pkg/cfn/manager/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ func (c *StackCollection) doCreateStackRequest(i *Stack, templateBody []byte, pa
input.SetCapabilities(stackCapabilitiesIAM)
}

if cfnRole := c.provider.CloudFormationRoleARN(); cfnRole != "" {
input = input.SetRoleARN(cfnRole)
}

for k, v := range parameters {
p := &cloudformation.Parameter{
ParameterKey: aws.String(k),
Expand Down Expand Up @@ -205,6 +209,10 @@ func (c *StackCollection) DeleteStack(name string) (*Stack, error) {
StackName: i.StackId,
}

if cfnRole := c.provider.CloudFormationRoleARN(); cfnRole != "" {
input = input.SetRoleARN(cfnRole)
}

if _, err := c.provider.CloudFormation().DeleteStack(input); err != nil {
return nil, errors.Wrapf(err, "not able to delete stack %q", name)
}
Expand Down Expand Up @@ -271,6 +279,10 @@ func (c *StackCollection) doCreateChangeSetRequest(i *Stack, action string, desc
input.SetCapabilities(stackCapabilitiesIAM)
}

if cfnRole := c.provider.CloudFormationRoleARN(); cfnRole != "" {
input.SetRoleARN(cfnRole)
}

for k, v := range parameters {
p := &cloudformation.Parameter{
ParameterKey: aws.String(k),
Expand Down
5 changes: 5 additions & 0 deletions pkg/ctl/cmdutils/cmdutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ func AddRegionFlag(fs *pflag.FlagSet, p *api.ProviderConfig) {
fs.StringVarP(&p.Region, "region", "r", "", "AWS region")
}

// AddCFNRoleARNFlag adds common --cfn-role-arn flag
func AddCFNRoleARNFlag(fs *pflag.FlagSet, p *api.ProviderConfig) {
fs.StringVar(&p.CloudFormationRoleARN, "cfn-role-arn", "", "IAM role used by CloudFormation to call AWS API on your behalf")
}

// AddCommonFlagsForKubeconfig adds common flags for controlling how output kubeconfig is written
func AddCommonFlagsForKubeconfig(fs *pflag.FlagSet, outputPath *string, setContext, autoPath *bool, exampleName string) {
fs.StringVar(outputPath, "kubeconfig", kubeconfig.DefaultPath, "path to write kubeconfig (incompatible with --auto-kubeconfig)")
Expand Down
1 change: 1 addition & 0 deletions pkg/ctl/create/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ func createClusterCmd(g *cmdutils.Grouping) *cobra.Command {
fs.StringVarP(&cfg.Metadata.Name, "name", "n", "", fmt.Sprintf("EKS cluster name (generated if unspecified, e.g. %q)", exampleClusterName))
fs.StringToStringVarP(&cfg.Metadata.Tags, "tags", "", map[string]string{}, `A list of KV pairs used to tag the AWS resources (e.g. "Owner=John Doe,Team=Some Team")`)
cmdutils.AddRegionFlag(fs, p)
cmdutils.AddCFNRoleARNFlag(fs, p)
fs.StringSliceVar(&availabilityZones, "zones", nil, "(auto-select if unspecified)")
fs.StringVar(&cfg.Metadata.Version, "version", api.LatestVersion, fmt.Sprintf("Kubernetes version (valid options: %s)", strings.Join(api.SupportedVersions(), ",")))
})
Expand Down
1 change: 1 addition & 0 deletions pkg/ctl/delete/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func deleteClusterCmd() *cobra.Command {
group.InFlagSet("General", func(fs *pflag.FlagSet) {
fs.StringVarP(&cfg.Metadata.Name, "name", "n", "", "EKS cluster name (required)")
cmdutils.AddRegionFlag(fs, p)
cmdutils.AddCFNRoleARNFlag(fs, p)
fs.BoolVarP(&waitDelete, "wait", "w", false, "Wait for deletion of all resources before exiting")
})

Expand Down
3 changes: 3 additions & 0 deletions pkg/eks/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ type ProviderServices struct {
// CloudFormation returns a representation of the CloudFormation API
func (p ProviderServices) CloudFormation() cloudformationiface.CloudFormationAPI { return p.cfn }

// CloudFormationRoleARN returns, if any, a service role used by CloudFormation to call AWS API on your behalf
func (p ProviderServices) CloudFormationRoleARN() string { return p.spec.CloudFormationRoleARN }

// EKS returns a representation of the EKS API
func (p ProviderServices) EKS() eksiface.EKSAPI { return p.eks }

Expand Down
3 changes: 3 additions & 0 deletions pkg/eks/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ func (c *ClusterMeta) LogString() string {
// ClusterProvider is the interface to AWS APIs
type ClusterProvider interface {
CloudFormation() cloudformationiface.CloudFormationAPI
CloudFormationRoleARN() string
EKS() eksiface.EKSAPI
EC2() ec2iface.EC2API
STS() stsiface.STSAPI
Expand All @@ -98,6 +99,8 @@ type ClusterProvider interface {

// ProviderConfig holds global parameters for all interactions with AWS APIs
type ProviderConfig struct {
CloudFormationRoleARN string

Region string
Profile string
WaitTimeout time.Duration
Expand Down
5 changes: 5 additions & 0 deletions pkg/testutils/mock_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (

// MockProvider stores the mocked APIs
type MockProvider struct {
cfnRoleARN string

cfn *mocks.CloudFormationAPI
eks *mocks.EKSAPI
ec2 *mocks.EC2API
Expand All @@ -39,6 +41,9 @@ var ProviderConfig = &api.ProviderConfig{
// CloudFormation returns a representation of the CloudFormation API
func (m MockProvider) CloudFormation() cloudformationiface.CloudFormationAPI { return m.cfn }

// CloudFormationRoleARN returns, if any, a service role used by CloudFormation to call AWS API on your behalf
func (m MockProvider) CloudFormationRoleARN() string { return m.cfnRoleARN }

// MockCloudFormation returns a mocked CloudFormation API
func (m MockProvider) MockCloudFormation() *mocks.CloudFormationAPI {
return m.CloudFormation().(*mocks.CloudFormationAPI)
Expand Down

0 comments on commit c9a10f0

Please sign in to comment.