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

IAM Policy Conditions are rendered as ['null'] #157

Closed
randomvariable opened this issue Jan 23, 2019 · 6 comments
Closed

IAM Policy Conditions are rendered as ['null'] #157

randomvariable opened this issue Jan 23, 2019 · 6 comments

Comments

@randomvariable
Copy link

In CFN, IAM Policy conditions are represented in a PolicyDocument object with a Condition key.

Unfortunately, GoFormation interprets this as a Condition intrinsic and attempts to process it, fails and represents the output as null.

We are using a workaround based on string replacement, but would be good to solve properly.

@grahamjenson
Copy link
Contributor

grahamjenson commented Jan 25, 2019

Can you give an example in YAML of the file you are trying to process. I have a PR that sounds like it is trying to solve a similar problem #158 It doe not support Condidtion yet though.

Goformation can store intrinsic objects as Base64 encoded strings in the Gostruct and will decode those to the properly when using template.JSON or template.YAML. This might be helpful for a workaround.

Or you could use the encoding intrinsics:

template, err := goformation.ParseYAMLWithOptions(input, &intrinsics.ProcessorOptions{
					IntrinsicHandlerOverrides: cloudformation.EncoderIntrinsics,
				})

Or turn off all intrinsics processing all together:

template, err := goformation.ParseYAMLWithOptions(input, &intrinsics.ProcessorOptions{
                                       NoProcess: true
				})

@randomvariable
Copy link
Author

We're using the Go template, so start with an IAM policy document with the following statement:

			{
				Effect: iam.EffectAllow,
				Resource: iam.Resources{fmt.Sprintf(
					"arn:aws:iam::%s:role/aws-service-role/elasticloadbalancing.amazonaws.com/AWSServiceRoleForElasticLoadBalancing",
					accountID),
				},
				Action: iam.Actions{
					"iam:CreateServiceLinkedRole",
				},
				Condition: iam.Conditions{
					"StringLike": map[string]string{"iam:AWSServiceName": "elasticloadbalancing.amazonaws.com"},
				},
			},

we should end up with a resultant template with the following resource:

  AWSIAMManagedPolicyControllers:
    Properties:
      Description: For the Kubernetes Cluster API Provider AWS Controllers
      Groups:
      - Ref: AWSIAMGroupBootstrapper
      ManagedPolicyName: controllers.cluster-api-provider-aws.sigs.k8s.io
      PolicyDocument:
        Statement:
        - Action:
          - ec2:AllocateAddress
          - elasticloadbalancing:RegisterInstancesWithLoadBalancer
          Effect: Allow
          Resource:
          - '*'
        - Action:
          - iam:CreateServiceLinkedRole
          Effect: Allow
          Condition:
            StringLike:
              iam:AWSServiceName: elasticloadbalancing.amazonaws.com
          Resource:
          - arn:aws:iam::012345678901:role/aws-service-role/elasticloadbalancing.amazonaws.com/AWSServiceRoleForElasticLoadBalancing
        Version: "2012-10-17"
      Roles:
      - Ref: AWSIAMRoleControllers
      - Ref: AWSIAMRoleControlPlane
    Type: AWS::IAM::ManagedPolicy

but instead get:

  AWSIAMManagedPolicyControllers:
    Properties:
      Description: For the Kubernetes Cluster API Provider AWS Controllers
      Groups:
      - Ref: AWSIAMGroupBootstrapper
      ManagedPolicyName: controllers.cluster-api-provider-aws.sigs.k8s.io
      PolicyDocument:
        Statement:
        - Action:
          - ec2:AllocateAddress
          - elasticloadbalancing:RegisterInstancesWithLoadBalancer
          Effect: Allow
          Resource:
          - '*'
        - null
      Roles:
      - Ref: AWSIAMRoleControllers
      - Ref: AWSIAMRoleControlPlane
    Type: AWS::IAM::ManagedPolicy

I did try going down the NoProcess: true route, but ended up with Base64 encoded strings for where we're really using intrinsics with Ref.

@grahamjenson
Copy link
Contributor

My PR just got merged so you could try

template, err := goformation.ParseYAMLWithOptions(input, &intrinsics.ProcessorOptions{
					IntrinsicHandlerOverrides: cloudformation.EncoderIntrinsics,
				})

To render a template with the base64 decoded you have to use either template.JSON or template.YAML, you cannot render it directly from the json.Marshal.

@randomvariable
Copy link
Author

randomvariable commented Jan 25, 2019

Got the same result. Did the following

func reparseTemplate(t *cloudformation.Template) (*cloudformation.Template, error) {
	j, err := json.MarshalIndent(t, "", "  ")
	if err != nil {
		return nil, err
	}

	rendered, err := intrinsics.ProcessJSON(j, &intrinsics.ProcessorOptions{
		NoProcess: true,
	})
	if err != nil {
		return nil, err
	}
	return goformation.ParseJSONWithOptions(
		rendered, &intrinsics.ProcessorOptions{
			IntrinsicHandlerOverrides: cloudformation.EncoderIntrinsics,
		},
	)
}

// YAMLWithoutConditions returns rendered GoFormation without Condition
// intrinsic function processing
func YAMLWithoutConditions(t *cloudformation.Template) ([]byte, error) {
	reparsed, err := reparseTemplate(t)
	if err != nil {
		return nil, err
	}

	return reparsed.YAML()
}

and then in the command line:

			template := cloudformation.BootstrapTemplate(args[0])
			j, err := cloudformation.YAMLWithoutConditions(template)
			if err != nil {
				return err
			}

@randomvariable
Copy link
Author

Explicitly, see the commit here: kubernetes-sigs/cluster-api-provider-aws@1a73f44

@randomvariable
Copy link
Author

Appears to be resolved by v3 at least

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants