Thinking About CloudFormation LanguageTransformations
ACM.293 The implications of letting someone rewrite your code
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
⚙️ Check out my series on Automating Cybersecurity Metrics | Code.
🔒 Related Stories: AWS Security | Application Security | Secure Code
💻 Free Content on Jobs in Cybersecurity | ✉️ Sign up for the Email List
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In the last post I set out to try the new AWS ForEach construct and add a loop to my CloudFormation KMS Key template. It didn’t work.
After spending way too much time troubleshooting, possibly due to misleading error messages or my misunderstanding of the documentation initially or lack thereof, I came to the realization that a loop only lets you create resources. Since a policy statement is not a resource (I think it should be as it would solve a lot of problems) and a ForEach can only create resources I was stymied.
But there’s a much easier way to do what I was trying to do in the first place. It was an interesting exercise but there’s a much easier way to achieve what I was trying to do, potentially. I’m going to give that a shot in the next post.
This was my first use of LanguageExtensions. I simply did not have a need for them previously. After using them I am somewhat doubtful I will use them again in the near future. I had too many issues and I’m not sure I like the implementation just yet. I’m still trying to put my finger on what bothers me but here’s some of it. (Well, as I got to the end of this post I kind of figured out what is bothering me.)
In the first post, I had a problem with not being able to use the language extensions. I found this implementation to be odd. It seems like a misuse of an IAM Policy and makes me wonder about who is managing the security of LanguageTransformations. You have to add permission to access an AWS Resource to your stack. That seems odd. It seems like it should be another action that you want to allow instead. I don’t recall ever adding an AWS resource in the resources section of a policy document prior to this. It just doesn’t feel right.
The action that failed because I did not have permission was not in the AWS CloudTrail logs. No trace that permission failure occurred existed in CloudTrail logs. What I figured out was that the code is likely parsing everything client side before you even send the code to the console to deploy the stack.
But how did that code check for permissions? There should be some record of that in the CloudTrail logs right? I mean you can’t tell I don’t have permissions unless you look at the policy. Was that in the logs? I don’t recall and I’m not going to go check now, but any access to my policies or accounts should be logged, and it seems like if I was doing something I don’t have permissions to do and permissions were checked, there should be a related error in CloudTrail.
All this seems a bit hacky and not aligned with how IAM was designed and is missing some key principles — like log every access failure (which helps with troubleshooting and threat detection) and you don’t generally add permission to use AWS resources — that is granted via the action. I hope that some tribal knowledge of IAM has not been lost at AWS in the implementation of this particular feature.
The other thing is this. LanguageExtensions are not simply adding new capabilities to AWS CloudFormation. They are re-writing your CloudFormation code under the hood. When you write a ForEach loop, the language transformations take that and generate old-school CloudFormation with multiple resources behind the scenes. They show you that in the documentation.
I am pondering this. Is it good or bad?
The thing is, you could write a code generator for CloudFormation and I think that’s a good thing. Awesome. Generate that complex code for me and minimize my myriad of mistakes. However this ForEach code generating construct took me an inordinate amount of time because it is complex, has error messages that didn’t make sense to me, I had numerous other errors in my CloudFormation template to simply try to get it working. And then it didn’t work for my use case anyway. I think it should have. I mean it should just be generating a snippet of valid YAML and let the core of CloudFormation figure out if the resource is valid or not. That’s where the errors should come from at the end of the day and be accurate.
And on that note, the errors were masked — and not in a good way. The errors were more confusing, not less, when bubbled up from CloudFormation. I had to remove the LanguageTransforms to get to the actual CloudFormation error which often made more sense to me than what the transforms were displaying.
Now mind you — it could be the other way around — and that would be lovely. There’s this pattern called the Facade Pattern in object oriented programming design where a front-facing object removes some of the complexity of the architecture behind it. Used correctly, this pattern could help put a new face on CloudFormation and fix all the cryptic error messages by providing something more accurate and meaningful. That was not my experience when using the LanguageTransformations.
That could be fixed — but it requires a LOT of detailed testing. Do not glaze over the error messages. This is one of the most important part of your user experience in CloudFormation — and I met an amazing UX designer on my recent trip to Amazon so I know you have them! Consider the person reading the message, how they will interpret it, and how easy it will be for them to fix that problem. If you want to know what my experience was like read the blog posts above.
I know. I’m a terrible example. I try to do everything too fast. Because I want everything to be fast. Sometimes I read too fast and end up skipping over words or reading things backwards. Perhaps it is an undiagnosed learning disability. But it also helps me see between the lines in some cases which can be a useful thing when it comes to security. And sometimes I do actually get things done faster by trial and error than reading overly vebose documentation.
Overall, my experience with AWS LanguageTransformations was not great. But I’m not giving up on it just yet. I like something about the concept.
I wrote about how I would love for AWS to allow me to pass in an optional parameter to a CloudFormation template and handle the whole messy !If logic that seems extraneous for me. If the resource property is AWS::NoValue, don’t set it. That seems obvious doesn’t it? Instead I have to set a default value, add a condition, and write bunch of messy if logic in CloudFormation — and I always seem to mess that up with some kind of typo — which is why I always copy and paste it from another template. And if I am copying and pasting it all the time, why is that not just a feature of CloudFormation.
Please add a “Required” property to parameters: true or false. If it’s not required and not set, don’t set the property. #awswishlist
I think that one change would save me a bazillion hours.
That’s an example of reducing complexity without changing the CloudFormation I write in a significant way. I can still write the CloudFormation and check it into source control with that approach.
I can still see the parameters passed into the template in the CloudFormation and compare what got deployed to what I see in my account (CloudFormation drift detection.)
What is AWS LanguageTransformations actually doing?
It’s rewriting your code.
It doesn’t seem like it is just generating code for you that you can then check into your own source control and deploy, so you can track any changes to the resources. It’s taking your code and it’s generating new code.
Is that OK?
Maybe. Or “it depends” as security people like to say. 😊
Can you see exactly what it generated and what got deployed in the CloudFormation console? Do you see your code, or the generated code? Both would be nice. I never got to the point of deployment since what I was trying to do was not possible.
What happens when someone changes that transformation library behind the scenes. Will you know that on the next deployment your resources will be different? Is there some kind of warning that the transformation code generated something different than it did the time before? Perhaps there should be an option for a diff if the underlying code generation library changes.
This is akin to using the AWS managed IAM policies. It’s a good place to start but I recommend to customers that they create their own policies and track them in source control because AWS can change those managed policies without telling you. They have to do that. If they add a new action and feature for S3 then they need to change their policy to allow it. But you, on the other hand, may want to review that new feature and its capabilities before you unleash your developers to use it.
I was thinking about how AWS CloudFormation works in comparison to XSLT — a powerful tool I used to love to use for generating static website code. I picked up a book on the topic and read it and was instantly sold on this technology being an absolute game changer. For me it was.
I could generate any static website with any design you wanted in a website content management system. You were no longer limited to picking from a limited set of components. I could deploy ANY web design with my content management system. In the end, you ended up with the following:
- The XML data that fed into the system.
- The XSLT template that transformed the data.
- The HTML template.
Of course people have moved on from XML and I moved on to other things as well. But if you think about what AWS Language Transforms is, it’s really trying to do a twist on that.
- Data plus transformation code
- Transformation engine
- CloudFormation template
Perhaps there needs to be some separation of those elements to make a clear delineation. The customer still need to see and deploy the results — the CloudFormation template. But there’s a way to generate that code that gets deployed that makes things easier.
I don’t think AWS LanguageTransformations yet hits that mark, but it possibly could with some tweaking.
Just let me see and check in the resulting CloudFormation template (like my resulting static HTML website code) into my own source control and do a diff on it. Let me see the versions, what changed, and see if those versions match what currently exists in my AWS account using CloudFormation drift detection.
Maybe I’ll revisit AWS LanguageTransforms later but my first impression was a bit painful. It could use a little more love. ❤️
Follow for updates.
Teri Radichel | © 2nd Sight Lab 2023
About Teri Radichel:
~~~~~~~~~~~~~~~~~~~~
⭐️ Author: Cybersecurity Books
⭐️ Presentations: Presentations by Teri Radichel
⭐️ Recognition: SANS Award, AWS Security Hero, IANS Faculty
⭐️ Certifications: SANS ~ GSE 240
⭐️ Education: BA Business, Master of Software Engineering, Master of Infosec
⭐️ Company: Penetration Tests, Assessments, Phone Consulting ~ 2nd Sight LabNeed Help With Cybersecurity, Cloud, or Application Security?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
🔒 Request a penetration test or security assessment
🔒 Schedule a consulting call
🔒 Cybersecurity Speaker for PresentationFollow for more stories like this:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
❤️ Sign Up my Medium Email List
❤️ Twitter: @teriradichel
❤️ LinkedIn: https://www.linkedin.com/in/teriradichel
❤️ Mastodon: @teriradichel@infosec.exchange
❤️ Facebook: 2nd Sight Lab
❤️ YouTube: @2ndsightlab
