Hey Friends. Azure Resource Manager is the deployment and management service for Azure. And you can use ARM templates to define a set of Azure resources for easier, repeatable deployments. But as much fun as it is to write JSON, you could use Bicep, which is a domain specific language with the power of ARM, but an easier authoring experience. Alex Frankel is here to show me how it works today on Azure Friday. Hey, friends, I'm Scott Hanselman and it's Azure Friday. I'm here with Alex Frankel. How are you, sir? I'm good, Scott. How are you? I'm very well. You know, I've actually been learning a lot about ARM templates and Bicep. You know, I've used them a little bit, but I feel like there's kind of an 8020 rule with with the arm. You know, you can learn 20%, you can get most of your stuff done, but it's a deep hole and there's a lot going on. And I hear Bicep makes it even easier if we're doing our best. We think it makes it at least a little bit easier. Yeah. So these ARM templates are declarative, right? They're not procedural. This is not a programing language in JSON. It's a declaration of what you want, right? You're saying exactly what you want, not how you want it to be put together. Okay. And then in the past, there was a thing called resource manager in Azure, and you would tell Azure maybe one of these, maybe one of those. This is kind of like expressing the shape of a solution. Right? You say, I kind of want it to look like this. These things are interacting. I want that network and I need this value to go over there. And it just figures it out. It's like Declaration of State. Yeah, exactly. So the benefit of being able to describe it all at once and say to Azure all at once, here's my final state. We can do a lot of things for you so we can attempt to deploy things in parallel as much as possible. Where they make sense, we can validate a lot more upfront. So if you're going to deploy a thousand VMs and on the 800th VM, you're going to exceed your core quota for the subscription. We can tell you that before the deployment starts, as opposed to get into VM 800 and then finding out that it's going to fail, a lot of things of that nature. So, so with great power comes great complexity and a lot of JSON. Why do people have problems or challenges with ARM and what do we do to solve that? Yeah, so I mean, the principal challenge with ARM templates, it is a language of sorts. You can have expressions, but they're embedded inside of these JSON files. And so you need to figure out how to contort the ARM template language inside of JSON. And that's where it gets really challenging and that's where there's a lot of opportunity to kind of run into to mistakes and errors and things like that. And that's where with Bicep we can kind of remove a lot of that complexity and just focus on describing Azure infrastructure without having to worry about fitting inside of these JSON files. Okay. So this is an analogy. It may work. It may not. When I think of JavaScript, it's not the language we deserve, but it's the language that we have. And then we created TypeScript, which then transpiles into JavaScript and trans and TypeScript uses modern, modern language concepts. Does that analogy work? Bicep then transpiles into arm? Yeah, exactly. So the analogy is exactly the same for Bicep and ARM templates. We're really just bringing a lot of modern programing language features, particularly around safety and validation and types in Bicep that you don't get in ARM templates. But ultimately when you send the thing to arm, it is being translated to an ARM template. Either you can do it yourself, but most people that had happened implicitly under the covers. Okay, cool. So let's, let's see what that looks like. I want to learn Bicep. Cool. Let's do it. So I'm going to switch over to be a skirt. I figured we would start from scratch and do like a blank project and then I can switch over to, like, Bicep in real life and we can show what a more advanced project looks like. So I'm just going to start by creating my main Bicep file and a couple of things happen right away, right? So I get my nice little file icon here. The code recognizes that it is the Bicep language with the dot Bicep file extension and then I just have this blank Bicep file. But because Vsco is recognizing that the Bicep language service is now running in the background, so any mature modern programing language has a language service that is helping you validate IntelliSense completions, all those sorts of things. We do the same thing for you in Bicep because it's really built in the same way that modern programing languages are. Did you install an extension to get this to light up? Yes. So let's look at extensions. And I have Bicep here. So so Bicep is in the voice code marketplace and you can just install it and then the code will prompt you to install automatically if you don't have it. Okay. And then because we're implementing there's a spec, there's a standard, the language service protocol because we've implemented everything using the language service protocol, we can pick it up and run this extension in other IDs. So we have first class support in Visual Studio, but the open source community of Bicep has got it to run in Sublime, has got a to run a neo VM. So all those sorts of things work as well. Very cool. Okay, let's write some code. So we're here to deploy resources. So we're going to start with the resource keyword. So I say I want a resource and you can see there's lots of like completions flying around already and we'll talk about what, what it's telling me, but immediately I'm going to give the resource a symbol name so I could call this foo. I could call this my storage. It's just a variable name. I'm going to call it STG and we're going to reference it later. Did you have something you wanted to bring up? No, I'm just absorbing. Okay. So the resource is a keyword. I see that it's purple STG is just a name, whatever. And then immediately the type pops up. So it's like, you know, you know it foo but in this case here that that's a list of all the different things Azure could potentially give you, right? So these are all the Azure resource types exactly as they appear in the ARM API for better or for worse. Right. This is a very long list. There's thousands of Azure resource types. There's probably been two new resource types launch since we started our conversation. What I can do is filter this list down, right? So I can just start typing storage accounts. And these are all the different storage account things. In Azure. I can see things like Blob services and encryption scopes and all all these other things, but I just want a basic top level storage account so I can just select. It's interesting that these are strings I'm extrapolating here because by making them strings that means that it's open ended and you don't need to read the Biceps back because it's not an inhuman as it is a string that gets passed downstream. Is that correct? That's right. So with Bicep and with ARM templates, we really we sometimes use the word like transparent abstraction. So we want to make things as easy as possible and take away as much from you as possible. But we never want to hide the platform. So if there is a new resource type tomorrow, you can author it in Bicep. If there's a private preview or whatever, it's all as long as it's available in the ARM API, you can use it in Bicep. Now there are times when so what we do is all the resource types in Azure, they publish a swagger or rest API specification. We ingest all of those and turn them into Bicep types and that's how they show up here with IntelliSense. But if, you know, tomorrow someone came out with a new resource type, maybe they didn't publish their swagger, or maybe we have an auto generated new types. Yet it's still possible to author it in Bicep. So let's say there was. I'm going to pick an API version that doesn't exist yet. Let's say there was 2020 20601 that you know exists, but we haven't had a type for it yet. You can still deploy it with Bicep. We just can't do as much validation on your behalf. Okay. Because I was trying to get a sense that it's not JSON, it's Bicep, but I'm familiar with things like JSON schema. But then you're using the context of the the Swagger API, but it's basically a type definition that you can suck in dynamically or otherwise that then informs your IntelliSense and I could type in whatever I want here. I just wouldn't have that, that upfront type safety. Right, exactly. So because I'm going to use a type definition that Bicep knows about, I will get all the completions and validations as I continue to type this out. So I'm going to do equals and then I get more completions. So I get a few options around like four expressions and if conditionals, which we're not going to demo today, but you can if you don't want one sword, you can't be 110 source accounts, you can do those sorts of things. Then I have something called the required properties. So this is again where we're taking those type definitions. We know which properties are required based on what the storage account API says is required. So I can do this all at once or I can just get an empty object and use completions as a go through, which is what I'm going to do. So first I need a name that's a required property, I can just show that. So I have name here and I can see that it knows that it's a string and it knows that it's required. What it can't do is like storage accounts have to be globally unique, right? Because they resolve to a URL so Bicep doesn't know all the storage account names that have been taken and can check that you have to come up with a way of creating a unique stream. So what I'm going to do actually, is I'm going to start my string here and then I'm going to use string interpolation to construct a concatenated string with some expressions. So I'm going to start it off by pre fixing my alias here and then I'm going to do dollar and then open bracket and now I can do any Bicep expression. So I'll do unique string and then I'm going to use the resource group ID as the seed for the string so that it will be as unique as I can make it. I need a location. So all, most resource that's natural need location. So I'll do normally I would do something like resource group dot location because I don't really want to worry about location. Now I'm getting a little yellow squiggly and we can look at what that's actually telling me, which is saying essentially use a parameter for a location, don't use a fixed expression even though that it would be a dynamic and this is our winter trying to tell you best practices. So the idea here is that if you're going to use this file anywhere elsewhere, you want to be able to override the location yourself. So what we're going to do is just add a parameter. So the keyword there is params, I'm going to call location and I get a limited set of types. It's going to be a string and then I can set a default value. So I'll do equals resource group dot location and then I'm just going to replace this with my parameter location. So there's a lot to unpack here. So parameter is a parameter into the file as if the file were itself a function, but it's kind of like a PowerShell script or a bash script or something that you're, you're, you're getting parameters from external sources. Right, right. So when I go and deploy this file, so I usually deploy the CAA, there's all sorts of ways to deploy a Bicep file. I will say the parameter for a location is whatever, so I can override it at the point time. Okay. And then resource group isn't an intrinsic because it has a it has brackets, but it isn't intrinsic in the sense of you're inheriting the resource group that is being passed in itself as some kind of a not a parameter but an intrinsic parameter or contextual parameter. It's been exactly the environment variable almost. Yeah, exactly. Okay. Okay. So parameter location in this case, if it wasn't pulled out of that now, is it in fact a parameter or is it a variable at that point? It's a parameter. So let me actually let me finish authoring the storage account and then we can look at what's under the covers with the actual ARM template that we generate. That'll give us a sense of like mapping of what everything's looking like. Thanks. So I have a few other required properties. I have kind which is required. So this is where I select blob storage or storage B to all of these enum values. Even though there's still strings, it is a limited set of allowed values coming from those same API specifications. I need a skew. This just has. This is an object that has a name. Again, you don't need to remember any of this because by itself it's going to help you all through it. And I'll do premium RS which is the name of one of the SKUs, but I could pick from any of the ones that are available. Okay. And those aren't those are available as offerings, but they're not guaranteed from a capacity perspective until you insert the Bicep via arm upon the current status of the system, right? Yeah, exactly. That's one of those things that will check right before the deployment starts. Now, I can there are all sorts of properties on storage accounts that I can set, right? I can give it a managed identity if I want. There's all of these properties that are available. I don't know what half of them mean. I can do access tier, I can set my encryption level, I can do all these sorts of things. All of this is coming from the API specification as well. None of it is required. And you can see there was actually a squiggle on STG. I can I assume because you hadn't done anything with it yet. It was just a declared but unused variable. And no, that one was actually because basically what Bicep are saying is I think that there's a missing required property here in this case tool. Now it's a warning because sometimes the API specifications are wrong and we don't want to block you from proceeding if it's wrong. So that's why it's a warning and not in there. This is great though, because you're getting so much feedback from the language, like they're doing everything you can to save you time and you're not throwing magic strings into, you know, here's some JSON and it's all a magic string and good luck. We'll let you know how it goes. Yeah. Anything it can help you with, it helps you with. It does very much feel like typescript two JavaScript, Bicep two arm. Yeah. Yeah. And we're finding that for Bicep based appointments, we're seeing like on average like a 15% reduction in errors. So we're able to look at arm template based appointments and Bicep and four ARM templates. It was like 18% for Bicep, it's like two and a half percent. So we're trying to continue to to drive that down. Well. So yeah, so there's all those properties available. The last keyword I'll show before we convert it into an arm template is an output. So you want to create a storage account, but you want to get values from that storage account. You want the blob your right so you can start sending things to that storage account. So I'm going to create an output, I'm going to call it blob your I, it's also going to be a string and then I'm going to say it's equal to stag dot. So I'm using that symbol from the storage account and I can actually do dot property access just like a regular programing language. But I would expect to be able to do and I can go through here and I can say storage dot properties that primary endpoints dot blob your IOD or dot blob and I'm able to get all the completions all through all that. Interesting. Okay. So then I read correctly when I was looking at the documentation about arm. Arm itself doesn't explicitly understand dependencies. It's not building like a large transitive dependency tree because there's things it can't know until it does them. So you get kind of a Schrodinger's cloud kind of event situation. Where did it happen or did it not? Well, I'll let you know when I observe it here. You won't know that information until it's built, right? Right, exactly. So the storage account has to get deployed and then we'll know what the blob URI is. And that's why outputs in particular, that's one of the last parts of the evaluation of the template after all the other resources have been deployed. That's cool. Yeah. Okay, so let's look at what monstrosity of an ARM template this is already going to generate. So with with no disrespect to our friends. Yeah, yeah, yeah. Well, we work on that too, so I'm really just disparaging myself. But the point being, though, is that it's a very nice, clean declaration of what you want. I'd like a storage account. I'd like it on this premium. I'd like a unique string. Let me know what it is. Right. Make it so. Yeah, exactly. No, no noise, no extra brackets, no double quotes, all those sorts of things that we're used to from JSON. So within the Easy Clyde, there's an easy Bicep command now, so I can do easy Bicep builds and then point it to main Bicep. And this is going to do the transpiling is technically what it's called into the JSON file, which we'll see in just a second. And we can take a look at this. And this is just a regular ARM template. So the ARM Tools extension that we also work on is about to kick in and start highlighting things, but we can see some of the things that we've done right. We have our parameter called the location, which was a type string. Here's, here's the ugly arm template, expression, character. You need to do everything in square brackets. So there's that research group location expression. Here's my storage account, here's my concatenated name expression. So under the covers we use a format function. Some people might be familiar with the King Cat function. And then here's where ARM templates get particularly ugly and gnarly to get that blob. You I this is what you would have to write in an ARM template. You have to do a reference well to give it a resource ID and then you can start to pull properties from it. So this is all being expressed in that same Bicep file that we were just looking at. Yeah. And this is really important for people to understand. Like, you know, arm is incredibly powerful, it's incredibly deep. It's a fundamental part of how Azure works. But from a textual perspective here, it mixed a lot of metaphors. You get a lot of tunneled type systems within type systems. One of the examples I like to give is when people used to have Here's some XML and here's some HTML that's been encoded inside of some HTML and here's some JSON that was encoded inside of the H2 with the in line 32 is kind of a classic perfect storm of all the different things we get types within types, within types. And then you've even got a string format in there with another magic string in curly braces, all of that hidden for you. Let's go back to the Bicep and look how clean that is. All of line 32 disappears in line 12. Right? Right, exactly. That's cool. And what we're seeing between Bicep and arm templates is like a 40 to 60% reduction in characters. Well, it's not necessarily this is a bad example because I'm going from 13 lines to 35 lines. That's like a a better example than it usually is the problem. The problem is that it's the resources with all the properties like condense that down, but we can get rid of so many extra superfluous characters. That's all noise that you have to get passed in JSON now. It's just it just is what it is. Yeah, this is really easy to look at and honestly, like the hardest part, the part that took my brain a second was the resource group function. Just to understand that there are it's not an intrinsic variable. You didn't make it some magic word, but it is a one of many functions that's just available that goes and pulls things off the context. And now I get it. And I could go and probably write a Bicep template right now. Cool. That's great. So I have one more thing I want to show in the simple example, and then I have a couple of if we have time, a couple of the things that I want to show you. Sure, sure. So we'll get rid of this JSON file. So this is a perfectly good Bicep file for creating a storage account. I have a parameter for a location. Let's do one more parameter for name prefix. So instead of hard coding our friend, I want to have this be a name prefixed. And so now I have this parameter here and notice that it doesn't have a default value. So someone has to give that thing a value at some point and we'll see where that that comes in in just a second. So what I'm going to do is rename main top Bicep to storage, top Bicep, and now I'm going to create a new made up Bicep and what I'm going to do is reference that storage file because I like to have many small files in my projects. I've learned this over the years of software engineering that I want small maintainable chunks of code. So what I'm going to do is yet another new keyword. This one's going to be called a module and it's going to be called a studio toy. So modules have symbols just like resources do. And now what I get to choose from or the Bicep files and even the JSON files that are in my project that I can then consume. So I'm going to select storage, dot, Bicep, and now I can just reference that file, fill in the required properties and it'll automatically piece these things together. And so in that sense I have a multi file project, but I can keep everything kind of small and maintainable like I'm used to in other programing languages. Okay, so module in this context here is this like import in TypeScript or using in in C-sharp? Yeah. Yeah, exactly. It's a whole art picking keyword names. I've learned from from working on programing languages. But yeah, it's like is it a noun? Is it a verb? Yeah. There's all kinds of arguments that can be made, but it's familiar and I think that's what matters. Yeah. Yeah. So modules have a name, so I need to give it a name. This is not the name of the resources is the name of the module. And I can talk a little bit more about what's happening under the covers, but I'm going to call it FG Diploid and then I need to so I get a red squiggle saying Something's wrong, you're missing something. Didn't you just call it help me understand the just SDG deploy as a string versus as a keyword up there. Yeah, this is the name place and we're trying to get rid of it, but this is the one place where the ARM template abstraction leaks through. So under the covers we create a nested deployment. So I can show that once we transpiled, this is actually the name of the the inner deployment, the nested deployment that's inside of the larger main template deployment. It has to have a name, it has to exist. We're going to start automatically generating a name, right? But for now you have to declare the name on something I like on something from line one and give it a unique identifier. And I don't ever want to think about it again. That makes sense. Exactly. It's cool. So I get a red squiggle because I'm missing that parameter, right? I have to specify name, prefix. It's own. It knows that. So hang on. Because of that module, it's already looked at storage. It knows storage has a that's cool. It's not just not just for syntax highlighting. It knows. Yeah, it's it's Bicep is built like a traditional programing language so so you know Anders Heilsberg preeminent language designer who happens to work for Microsoft He helped design C#, helped design TypeScript. He really helped us understand like how does a programing language get put together? Like what should it be checking for? How should it be structured so you can evaluate basically anything anywhere in whatever state your files might be in. And so you're seeing that come through in like the day to day of Bicep offering. That's cool. Okay. So I need to give it a the params property and it knows which one's required. It knows name prefix is required. So I can just say our friend here again the winters is warning me. Hey you want to make sure that those locations are dynamic? So what it's telling me is to have yet another location parameter here and then use that here and then this warning should go away. Oh, because it's not assigned explicit values. I need to give it a default. You get a default. And I was going to say yeah and that could be research group, that location which gets passed in from elsewhere or just hardcoded West U.S. or something. Yes, right. But I'm still getting a warning. So what is an assignment system? Is there any scoping? Is location as an identifier already used in storage? No, the scoping is all by module by file. This. You just reopen this and see if this is good stuff. Yeah. Mean these figuring these things out matters. Yes. Jump over into storage real quick. So we've expressed that you did use location there, but it's scope to this. So the location here is not the location over there. Right. And then you pass it in in six and this is like and now it's in include it's a module that's going to be included over into Bicep. It's been saved and we know about it. And then you've you removed the part where you put your name prefix and you call it in as a parameter. Okay, that makes sense. So then our friend gets passed in on main. So then when you return to Main, I see that you're passing it in. Oh, jeez, I'm all over the place. Right. You want to pass in our friend, which is different than your location? Yes. So I was combining all sorry I was trying to return to bare principles. First principle you help me debug and we did a live debugging. Well, this is why as your Friday's a real show and it's also a funny funny thing I know you keep pounding on us like this is a real language. It's not JSON, there's no commas after these name value pairs, right? Like and you're getting a lot of really rich help. It's telling you exactly what you need to know. Yeah. Yeah, it is. It is very helpful, just as you're going through basically everything. Okay, so this is what I was complaining about. You have this location parameter, you need to use it. Basically, it's book the winter was telling me and then just like the module output it that information this is a controlled use case I wouldn't just want to output it again here. Right. I could so I can say blob your I string now I can do a reference on the module symbol stg deploy dot outputs dot blob you array. And so now you know what I would normally may want to do is from one module I need one property, some connection string or whatever and I want to pass it to some other module or some other resource. So it becomes really easy to kind of wire all these resources together. Interesting. So I could see someone or a company, an organization building a large library of these that also expressed as best practices. This is how we name, this is how we create storage things. And I would just use them as functions like I need storage in whatever way the company that I work for likes to name them, create them, put them somewhere. There's a lot of times things get messy and I could see things becoming quite tidy if we all standardized on a Bicep library that we would use then. Right, exactly. And thank you for bringing that up. So we have we have the ability to pull in modules, not just locally but externally. So you can actually publish modules to a registry, whether it's your private enterprise registry or we have a public registry where we're starting to curate some content. So just like Nugget or NPM or whatever, you have code distribution with Bicep that is versioned and safe and all these sorts of things that we expect. Very cool. This makes a lot of sense. Cool. So we can now switch over. So this was like basics, just like understanding the absolute and of how Bicep works. I'm going to switch over to kind of like Bicep in real life example here and to show off a couple more features. So this repo, this is a personal project that I maintain my mom sells antiques. So I have a little like website where you can view all the different things she has on offer. It is a view front end and the world's worst node back end, but it works and I'm using container apps as my my compute mechanism in this particular case. So I have a deploy directory here and I have a few different files going on here. So I have one that's a CTP container dot Bicep. And really all it is is a lot of parameters and a lot of things like that, but it is a container app, so it is a instance, a service that I'm running as part of my application and I have a lot of parameters here with defaults and things of that nature. But for the sake of this conversation, it's a file that deploys a container app. At the end of the day, I love that you've even got in line 36 there too. Do I have no idea what this is? Yes. Yes, plenty of that as well, but it seems to need to be there. So yeah, I think I think that said, AI enabled is a termination of https at the endpoint. So as opposed to you know, I see. Yeah, this is great. So Azure Container Apps is Kubernetes for the people, right? I mean, like you don't need to even worry about that. You closed that up, collapsed 22 to 72, and now you're throwing in containers about us. And I don't know about that. We don't care about that. That's great. So what I'm going to do with this file is I have this resources, not Bicep fire, which does a bunch of different things. But what I'm doing here is I'm reusing that container app top Bicep file so I can have one container which represents my front end and another which represents my back end. And I just give it different settings and that's cool. The main one being the container image itself. So I have my ACR, there's a lot of referencing going on here, but I have my MFA front image name and then I happened to be getting that get hash because every time I check in, I'm building a new version. And so that hash represents the latest version of this particular front end. So I have two modules here, the front end and the back end, but I have some errors. So we want to correct those errors before we deploy. I need the Container Registry username and Container Registry password. So I'm pulling this image from ACR. It's their private images, so I need to authenticate it. I need to figure out a way to talk to this. So let's I can right click and go to definition because that works. Mm hmm. So I have an ACR instance here. Now, the big thing here is that I'm not creating the ACR. I'm referencing an existing ACR that was already created. So this is just to get values from it, whether it's image names or we'll get usernames and passwords. In this particular case, if I go back down to my red squiggles, I need to get the container register. Username ACR exposes a function which I can use and find with IntelliSense called the list credentials. And so I do list credentials and then I can do dot username in this particular case and then same thing for the password. I can do ACR NatWest credentials and you're safely and securely doing things like this now and no one's going to get mad when you checking into GitHub right? Exactly. So I'm able to make sure that these are all real values. I don't need to necessarily know the exact structure that list credentials, returns. I can just understand and dynamically as I'm authoring it. So now this file is happy. I can move on from this. And then the last thing I want to show, I have another red squiggle down here. I have a key and a password that I can't get dynamically from from another resource. They're real secrets. Someone has to rotate them. They happen to live in Kibo. So I'm going to do another existing reference here to a preexisting key vault and now I'm going to use one more function. This is like a special function that only exists in Bicep to get a secret. So I can do cave dot get secret and then I have the two keys that I want to get. I have something called the cloud, an area which is storing images for me from the API key. And then I have one more secret which is GBE password cloud energy being in images and media can for, you know, for all of the antique pictures. Exactly. And I can dynamically resize the images whenever I want. So that's a nice clean things for me. Cool. So now I'm able to to get secrets again all dynamically. I'm not checking in any credentials. I'm not storing any secrets. Even in my my GitHub project, it's all able to be kind of self contained within within Bicep. This is very cool. And then just to make sure that for folks that are watching, they may have been thinking, well, hang on, what's the difference between TerraForm and Mean Bicep? If I understand from what you said from this very excellent intro to Bicep here is that Bicep is a layer that sits directly on top of arm. It trends piles into arm and then ARM goes into the Azure machine. And Azure does the right thing with declarative format and creates stuff. Well, you're pulling these and your chair forms and things like that. They're actually turning into commands and they're executing against the cloud. It's a different philosophy. Yeah, I mean, people will debate this point in particular, like, is TerraForm declarative or imperative? Like I would say TerraForm is declarative. It's just that's the TerraForm engine that's taking care of the declarative ness for you instead of ARM rising. So describe what you want, not how you want is just a different processor, effectively making the decisions on your behalf in that case. Yeah, but I'm not saying you can. Can you write for loops like how when can can this go and turn into a kind of imperative? I did it for a loop, but it's still declarative at the end of the loop. Yeah. So we can go back to, just to make it easy, let's say we wanted, we could do all sorts of things, but let's say we wanted three storage accounts so I can do a four expression in Bicep. So it's a little funky to offer after the fact. But I do a bracket here and do a bracket here. I can say four I in the range 0 to 3 and now I have a loop expression and I have this a loop variable that I can use to make each instance unique. And then I can just do with string interpolation. I can just add the I there and now I'll get three or N or a thousand storage accounts and then notice what happened. I got some red squiggles as a result of doing that because STG just turned into an array of resources and not a single. Exactly. And you can see Bicep understands the type that it's actually an array of typed storage accounts. And so instead of doing STG properties, I could do SDG zero or I could construct an output for expression to do all three, blob your eyes so I can be as dynamic as I need to be. This is very cool. Well, thanks so much for hanging out with me today. Yeah, absolutely. I'm glad I got so to you. This is very cool. I am learning all about Bicep and how it relates to Azure Resource Manager with Alex Frankel, today on Azure Friday. Hey, thanks for watching this episode of Azure Friday. Now I need you to like it. Comment on it, tell your friends retweeted. Watch more Azure Friday.