Writing a DSL (Domain Specific Language) for PowerShell
Marquette, Kevin
Creating a Domain Specific Language can often address specific problem domains better then generic coding or scripting alone. Join me as I break down what it takes to create a DSL in Powershell and show you how to approach common design patterns
 Alright, I guess we'll go and get started. So today we're going to be talking about domain-specific languages and how to write one in PowerShell. My name is Kevin Marquette. I'm actually going to cover quite a bit of material. So some of the intro stuff, I might go through a little bit quick so we can actually get to the good stuff kind of in the middle. My slides and presentations are always up on GitHub. If you want to download it for the code samples that are presented in this presentation, feel free to do so. So what is a DSL? Right, a domain-specific language is used when you want to solve a problem in a specific domain space. Generally there's like terms and language around a certain problem domain that might actually help you solve that problem better than a general language can. So PowerShell is good as a jack of all trades. It can do many things. But sometimes you need a tool to do that one specific thing really, really well. And that's kind of where DSL's kind of answer that problem. We already have several DSL's in the PowerShell community. Like if you've been working with DSC and Pester, like those are great examples. I have several up here that I've seen in the community and I'm going to actually, we're going to walk through a couple of these real quick just to see what a DSL looks like in PowerShell. Okay. We all know and love DSC. And as we look at this as a DSL, we actually get to describe our problems there. Like you say, we're a configuration that's configuring a node that's setting up a Windows feature and has some like file web content. Like the language helps describe the problems that we're solving with it. And it does a really great job of doing that. Another DSL many of us are familiar with is Pester. It uses beautiful syntax where we can describe what it should do and using that term in the terminology, we actually focus in and write better tests using a syntax that is, that rolls off the tongue a little bit better. A couple more examples of DSL's is like invoke build. So if you use invoke build or sake, right, it's like a build automation DSL. It's built around if you have tasks that perform certain things in your build pipeline. And there's certain terms and nomenclature around that effort that is captured really well in both of those DSL's. Example near and dear to my heart is PSGraph from a module that I wrote that is implemented to describe a node edge-based relationship of things you want to diagram visually. So this one here, I actually describe three nodes and I can describe how they relate to each other and when you execute it, we can actually see the diagram based off exactly how we described it. And last example from the community is this plaster manifest. Has anybody worked with plaster? So for those of you that haven't, it's templates that you can use to generate functions or modules or anything, anything really. And right in the core of it is this manifest that's built in XML that describes what inputs to receive from the user and what files to transform and deploy. If you don't like working in it with XML, here is a community DSL that is specific to defining those configurations. This one here has the parameters and the content and if I take a quick peek at this, we should get a large, it's actually generating this XML for me to use as part of plaster. Like this is a great use case of a DSL. And I wanted to run through these just to kind of get a feel of, they look kind of unique, a little bit different from the PowerShell we're normally writing. And so let's take it a little bit deeper. Let's really deconstruct how some of these DSLs are doing the magic that they're doing. Because when we look at Pester, it's really just a function. That first command is just the command line like you would normally write. The next description that we use is a positional parameter. And then at the end, we've got a script block that hangs off the end. Both of them are positionals. So in a way, these are, this is PowerShell, but we're using it in a very different way than you would in most use cases. This pattern repeats through this DSL as well, where my, it does something, also has parameters that are positional, a script block that is also positional. And then our should statement is just another function that's taking pipeline input. And when you break it down that way, it's easier to process what's actually happening when you're running these DSLs. So we tear Pester down a little bit more. So I'm going to start with our same example here. Can I get this? So starting with our same example, if we look at the help, the syntax I've described, we can see that it is exactly how I described it. We've got a positional parameter name for our string. Our fixture is the parameter name for the script block. And as we take that knowledge, we can actually expand that Pester statement into like a fully, the more verbose syntax that PowerShell tends to use. And then here we see we have the name, our fixture, and we kind of repeat this, right? So all these things actually do have name parameters, but in the style of a DSL, we kind of don't use those. Now if we keep going down this path, right, and tear this part even more, imagine if this actually was more PowerShell-like, like more like the code you write that's more procedural. If we start from the bottom, could you imagine if we actually write our test this way, like if we write our described block and had to pass it a script block as a parameter, right? Or then our fixture itself contained another statement that took a script block. And I don't know about you, but I probably wouldn't have adopted Pester so fast if I had to write every one of my tests exactly this way. And as I start to see how a DSL can solve certain problems, I let you see the answers in a different way. Because when I compare this to something like this, I'm so glad that I can actually do my Pester tests like this. Now I'm sure if we had to do it the other way, we would have had different solutions for the problem, but this is the problem that a DSL can address. So we kind of covered a lot really quickly there. There's a lot of great points when you just start deconstructing those DSLs in that way. And the first is, like I said, DSL commands are just functions. And we also don't follow community best practices. It's really weird when we start writing a DSL because I've had years of, here's the best practice to do something. And I'm fighting all those for the right reason. But it feels like I'm running a really dirty code when I know it's really clean and insolvent problem. The names, right, we don't use the name verb so often. We name them specifically to match the problem domain that we're working in. We're always using these unnamed parameters or positional parameters. We'll often leave off the quotes in our syntax and style. And I say we abuse script blocks. I say it that way because you normally don't hang script blocks off the end of a function unless you're doing it in a DSL this way. I think I've gotten more used to that syntax. I'll do like an invoke command now and I'll hang the script blocks. But in the earlier days of DSLs, I don't think that was as common of a style. There's some other hacks you can do by hanging an array off the end or a hash table. I don't like it so much. But if that solves a specific solution and that syntax makes a lot of sense, it's viable to do that. I've used it once. I've kind of gotten away from it just because the best practice feels more like using a script block there than any other hanging structure. Then the other common pattern we see is a lot of DSLs have a invoke type function, like this helper tool that consumes the DSL a bit and wraps it in more functionality. Pester, you can run a describe block and you get that fancy output or the beautiful output of your tests. But you miss out on other features such as the number of tests that passed or failed or status of failure. They give you formatted XML output files, code coverage. Quite often those helper functions take the DSLs and actually add more value to those. Okay, so we've got a good picture in our mind of what the basics are and all the little pieces. If we're wanting to plan a DSL, the first question you're going to ask is make sure a DSL is what you need. Just because you can do something doesn't mean that you should. I know I've gotten excited and really enjoy working with those and I probably used DSLs way too much. It solves a problem in some cases and because you break your community best practices, it does confuse your audience unless they understand that domain that you're working in. Prototype the syntax before you code it. This is probably the most valuable tip that I've learned when writing and working with DSLs is write the code as if you're using it. Pretend like everything exists and get a feel for your interaction with it and do a couple different versions of it. See the syntax that you think you can implement and that fits really, really well with the flow you want. Yes? Can you say that's a general best practice for writing scripts or modules? We used to have teams write all their modules and then they'd come up all screwed up. Then we adopted the practice we called demo.text. Before you write anything, write off the demo.text. Show us what somebody type, what they'll get, what the flows will be and then we review that and that's where you find all your problems. Absolutely. You get such better consistency across stuff when you do the flow that way and save those samples and put those in your pester tests. What you guys are almost describing as well, if you come from the development world is test-read and develop. You write code using stuff that doesn't exist yet. See you have your failing test to begin with. You write your test so that you know what the output should be, which also fails because you don't even have anything that outputs. Then you write the code and at that point you are already documented both the input and the output for what you're describing. That's the middle step and the first step to demo.text is really just to focus in on the user experience itself. If you do the test, it's hard to see the experience. I'll take those prototypes and when I'm done, those feed right into your test flow. More like behavior if you're in development where you're testing what the user behavior will be. Okay. I always blur those too when I refer to them. Thank you. Okay. Let's actually build a DSL from the ground up. For this one, if you've used RDC man desktop connection manager, it actually uses an XML file to store all the information about your connections. All right. DSL planning. So there's the middle of it. Okay. So first let's prototype the commands we would use. I start with the PowerShell type syntax because maybe that's going to be my answer and it might actually be a better solution in this case, but we're making a DSL here. If I was going to do functions, I think this is kind of the flow I would come up with where I can define here's the servers to create this file or maybe a group that I could put the servers in. That'd be a good set of command lists. Let's think of this as a DSL. So my idea number two is we do something a little bit of a structure to it. I define my file with a group and maybe listen to the servers in it. Here I'm using that hanging array. I'm not really sold on that. And the more I play with it, I don't feel like that's quite the right solution here. So my third idea I came up with, and that's prototyping, was something more like this where I describe a group, you know, I'm calling this one production, and that group contains my server, my RDC server, server one and server two. And what I like about this is that if I wanted to extend this later and add other properties to this guy, knowing that he's just a function, we can add like say a describe or a function or a description to add more data to that, to our DSL later. Okay. So this is the syntax I've decided on. This is the one I commit to developing and planning for right here. As we continue on, I'll kind of expand out all the possible properties just so I know what I'm actually coding. So I sat down and made up these names here off of my template, off of my little piece of scratch code, and I'm now going to go implement those parameters. Is that a big track with me so far? Beautiful. Okay. Now, so the RDC man XML file, you can think of it as this large XML document. Inside that, it contains groups. That's another like XML structured, and that group contains XML servers, right? So I've got this outer template to this inner group, to this inner object. So the first one I'm going to write in this case is my RDC server. And my parameters, based off of our spec, I'm just taking the computer name now. This could be expanded to more parameters later. So it's positional, mandatory. I could take it from the pipeline, but that's another discussion. All right. So let's break down to our process looks like. So all I'm really doing in this example is wrapping a value around injecting it into some chunk of strings, which happens to be XML. I definitely could have actually done the XML node object and made full objects on this, but for the simplicity of this demo, this is what we want to do. It's much easier to follow. And all I'm really doing is creating a string and tossing it on the pipe for somebody else to deal with. I'll have another function that cares what's coming down to it. So I literally just have a small chunk of valid XML for my server. So I need to invoke this because we're actually going to run this here in a moment. All right, let's get that loaded. Then RDC group. So here's where we're starting a little bit of the fun. So I still have my group name as a positional parameter. Then our second one is a nether positional parameter in the search group block. I'm calling this one child item. I could probably pick a better name for that. And then I'm doing the same thing before. Like I have a small chunk of XML, I inject my value, toss in the pipe, and then I actually go to my script block and just say invoke the script block. Whatever's inside the script block, we're going to run that because there's other commands in there that need to execute and run. And in this case, this command doesn't care what happens. I'm just going to toss on the pipe as well. So somebody else upstream is going to handle this. And in a way, I've just got this giant string builder. When you really break it down to it, I'm building just giant strings. So let's load this guy up as well. So now the outer object. And I think he might be something fancy. He's actually just as plain as our group. There's just more XML overhead. I'm going to invoke my script block because it's like what the collection is, the child items, and more XML footer. So at the end, all I've done is just compile this XML dead and toss it down the pipe. But because I did my child items in the right way, I added the structure to it. So if I look at, let me go back up to this guy slightly here. Like you'll notice, I start a group, I have properties, and I know whatever I invoke in here definitely needs to give me valid XML for this to hold, for this to work. And I close my XML object. So let's run this one as well and see if this comes together like we expect. And that one. OK. So here is that first example we started with. Here's our prototype. When we take the next step, we'll save it to a file, and then we're actually going to open it up with remote desktop connection manager. So let's do this. Summing up from loaded properly. We execute that and actually created my RDC connection manager profile. And that's where it gets cool is this matches the description of what I wanted to generate. And that's where that DSL solved that problem really beautifully. We're having all those commands to do it. You can get that break from the pattern. And because of the way I wrote it, let's try something a little bit more complicated. Let's say we get a group more like this, where I'm defining, that's so tall. So I have got a group that contains a group that contains servers. Because the structure is valid the way we're doing our XML, I can actually, I can do this. So when we run this command right here, we get exactly what we described in RDC manager. So that's where DSLs get really cool is that's what I told it. That's what I wanted to see. And it aligns so perfectly with the output that I produced. I suppose any questions at this point? Well, following kind of what we built here? Yes? Just something I'm noticing that DSLs really sell in hierarchical data. Absolutely. And then we work in linear sort of like flat data. You might not get as much of a benefit in that case. But yeah, hierarchical stuff is also, that's why my jumps to DSLs way too fast. But that's where I find the best solutions of stuff that has a hierarchy to it. All right. And then if we wanted an invoke command for this, it might be something where we save those into like an rdg file. And we could run a, you know, build rdcman where it finds all those files and saves it to an output. I don't know that's actually useful in this case for DSL, but for many of them you'll see a pattern like that. All right. Time. Perfect. I can see a case where you're going to a client and you want to quickly get a console to have a list of all of the servers in the OU and give you the, the got the way for the patient. Actually, that's a great interaction because you'll be with the client and they can be telling you what, describing it to you and you'll be typing out in a way that they could almost understand it. And that's exactly it. We're using script blocks, right? And you can mix. That's one of the biggest discoveries I had with Pester was it truly was just PowerShell commands. And I could wrap all the PowerShell around that that I wanted, you know, for each statement or if logic, like I could decide to never run a test based off of the conditions. Now Pester supports that with its parameters, but there's a lot of cool things you can do knowing that it's just PowerShell. So because those are script blocks, couldn't you also dynamically generate stuff nested in the hierarchical structure? Yes. Yeah, like I said, that could. Concretely, what you could do is you could set, like that engagement you mentioned where you say a little bit of a lecture, or a little bit of a lecture. And you have that written up and that interloop of the servers and set up hard wiring. You could have a query out to AD to a specific OU and then generate. You just have a for loop and boom, the power is just crazy. Oh, you lost your stage, by the way. Yep. I'll see if it comes back up. Okay. So like this command, okay, I'm going off script here. We'll see how this works. I'll do one dot dot 10, and we'll say, oh, so I think. I think that's what I want. Maybe not. Just put a comment saying before the last part. That'll just give you strings. He needs the RDC server. Oh, I see. Thank you. I deleted the wrong thing. I couldn't see it when I was staring at it. That's what I wanted. All right. So this stuff can't be edited. And mentioned earlier, I've added pipeline support to that DSL command because that's my pattern. And I'm going off-strip on that too, but anytime I can actually reliably add pipeline support to build a process stuff like that, I get so much value out of it downstream. I'm writing it for one thing, but if I can design it for anything, I'll use it for anything later. All right. We lost. Okay. So that's my big demo. If I do have some DSL tricks, I want to call them design patterns, but I don't know if they're actually design patterns or not. But we'll kind of work through that. So sometimes I have a command for lack of a word, I'll call it like a value pass-through, where I'll actually create a DSL command name that takes a string and just returns that string because the syntax and flow for my user might make sense to actually use that word. A good example is my PSGraph. It's really just a wrapper in front of graphViz, which actually has its own DSL. And if you're an expert in graphViz and know the syntax for something that I don't support or maybe I do, I have an inline command, we say inline, and put the inlineDSL for graphViz. But under the hoods, you could have just put a string there and it would have worked anyways. So for the sake of my DSL, I added a command for inline, I put documentation and tell people how to use it. It's easier for them to discover than trying to tell them you could put random strings in your strip loss and this stuff would just work. So there's value in designing for the user. The next two, which I'll refer to as like the simple template and the nested template, is exactly what we wrote with this RDC man. The first one is just a value shoved into something that's wrapping it. We're using strings in this case, but it could be classes or objects or hash tables where you're getting the values shoving it into something and passing it up the string. Then what I mean by a nested template is one that does the same thing but executes the script block. And that's what my RDC group was, was something with like a nested templating pattern. Now some other tricks I've used in DSLs is like a, I'll call it a hash table pass through where I'll take parameters so they can write something like this. Like for like, what? Oh, changed again. You know what? PowerPoint has been playing games with me up here. Okay. So I could have a command like this where I say a state machine where I'm saying state started with a script block and upstream I just want a hash table and I found, I can actually just take all the properties and the parameters and just return the psbrown parameters up the stack for somebody to deal with as a quick way to create hash tables that are structured with the syntax that people can discover. Then that same note, I've used this pattern where I want a syntax that looks just like this which is just like DSC but I'm not using the dynamic keyword to do this. And I'll come back to that special approach later if I have time. But there's an alternate way to do that just in PowerShell where I build my script block and actually rebuild it in line as an ordered hash table. So I'm making some assumptions on my users that they'll use that syntax but if I want this to come out as a hash table through my pipeline or however I'm going to use it, I can literally say, you know what, let's just take my script block, make it a string, wrap it in order and execute it and return that object back. The value in that is that you can get that object the way you want to see it. So if I run this here and actually execute my sample up above, he's going to give me a hash table that looks, so I got a syntax that I want. I can get the data I want somewhere else. If I keep going down the list here, like an object collector, I mean, so far I've done a lot of taking objects and just tossing up the pipeline and not caring about it. And for string builders, sometimes your output is just everything tossed on the pipeline. There are other times though, I say if I'm building a state machine, we actually want to collect all those objects and do something with them. And it's actually as straightforward as you'd assume where you run your script block and you've executed it and you save it to a variable. So you have a variable full of objects that came down through your DSL and then you walk them and process them. If they're mixed objects, you want to do your is your types, but you want to walk the list and account for what's coming up the pipeline. So when I say somebody's going to handle that later, it might be this pattern that's handling it later to decide what to do with those. In this example, I built a state machine. I don't know if you actually demo this really well, but my DSL up above, this guy gives me hash tables with a state and a script block. And then I actually, my state machine builder, I collect those in a hash table based off the key is the state and the action is the actual hash table object that can be processed through a state machine. Then other fun stuff. All right. So maybe you want not any command to run, right? Like I'm using script blocks. People could put anything they want in that script block, any commands. And if you have a DSL where you're writing it to disk and you're making certain assumptions about what those files are, you can actually restrict what actually gets executed. I think initially with prior partial one, we had the data statement, early partial with this data statement, that anything in there was treated as data and could basically whitelist the commands to execute. I think more recently, you could also use the check restricted language feature of a script block to say the only commands, command lists or functions to run should be, you know, these commands that I'm defining right here. But now you can actually place control over what could arbitrarily be in that script block. Now you break the ability to do, like, you get AD, you know, computer to pipe into a server list, but you do gain trust in what's in those script blocks. The internal only commands. So maybe you don't want to leak all of your commands out into the command space where you want them to actually execute in a certain structure. One way to do that is actually if you define your DSL commands inside of another command, the scope holds it kind of in place. It just won't exist, or the outside of that. You lose your get help and discoverability on those commands doing tricks like that. Another thing you can do, a little bit expensive, they could check the PS call stack to see am I being called as a child element of, like, say, my RDC manager. Like, RDC group on his own has no value. So should he even run if he's not part of the larger structure? Is that how the tester makes sure that your is inside of a described or complex one? Probably. I don't actually know, but if I was going to make the assumption and implement myself, I'd probably check the call stack first. But that's all my list to, like, dive down and see exactly their approach. The dynamic keyword. Okay. So this is kind of like classes in PowerShell in that it was implemented specifically for DSC to be able to do what it does well. And it's kind of fuzzy when you use it for anything else. What the dynamic keyword does is it takes your commandlet and basically allows the AST to treat it as a command. And we've been using all these dangling strip blocks. Being promoted to a true command in the PowerShell AST allows that strip block to not hang anymore. So just like your if statement, for I think the first to be on the end or on the next line, creating it with the dynamic keyword would allow you to make it look more like a native PowerShell command. And you still have the option to have a strip block in there or use, like, a hash table. Like the DSC resource would be like a hash table dynamic keyword. And then you get things like autocomplete and syntax validation. You can define these are the keys that are only, that are possible in there. And the AST will tell you, will parse that and throw errors if something is used that you didn't define. So we have a nice tight structure. The dynamic keyword can kind of help you with that. But there are gotchas. And the first one is that it has to kind of exist before the script is parsed. So if you have a script where you're using it and you try to dot source your dynamic keyword logic into it, it's going to fail because it's going to parse your script first before it goes to the dot source. Very similar with classes. You kind of run into those same nuances. And you also can't currently use other function parameters. Like if you're working with Pester, you can do a describe and give it tags and have additional parameters on there or you can skip a test or it's a pending test. In my experimentation with dynamic keyword, I can't add, I can't also use those parameters. You kind of give up that. I think there's a third limitation because of the way dynamic keywords are implemented and that is that your variable scoping starts to have strange behaviors because the way this works is that it just takes a copy of ASD and sort of pours it into, it turns into a function for later on. I did some work. I'm on the power siding. Okay. Thank you. I'm going to give you the same word. Prototyping first class DSL support in PowerShell a year ago and took months for us to realize that there was another limitation here was in variable scoping because of the whole dynamic scope. Okay. Thank you. Now. I can't remember the specific type of word. I'm pointing this out for awareness. I don't actually use this anywhere because of limitations. As you're diving deep into DSLs, this is a thing to be aware of. And then the last detail is actually learn this one this week is if you add the attributes debugger step through, when somebody is debugging their scripts that uses your DSL, they'll actually jump from their code to their code in the script block. And they won't actually have to debug automatically through your DSL commands to get there. This affects automatic breakpoints. If you had a breakpoint on any time somebody says, you know, write outputs globally, your functions will actually be excluded from those global breakpoint type scenarios. All right. So I suppose any questions or things we want to touch on anymore? Yes. What are the pain points and caveats that you've seen defining DSLs this way? So the caveats are sometimes, well, sometimes I write really, it's like knowing best practices and doing it for so long. I feel like I'm writing a really ugly power show that's breaking a lot of best practices. That's more of a procedural process thing. I made, in my DSL, I made heavy use of positional parameters where I describe something that's a string, like I describe something that has a string like a node. And I also have like a hash table that is optional. Like I still had like sliding parameters. Actually, my subgraph would be the better example. I could have a string, a positional hash table, and a positional script block. But I made the hash table optional and the string optional. So I have like six parameter sets to allow for this weird nuance of the user can choose a script block or the hash table and the script. And that made sense in the context of that DSL. Yeah. We're relying on those visual parameters. We're kind of enforcing a sort of structure to the DSL, especially if you have two parameters that are script blocks and make the future loss of the task. We're expecting one script block to be hanging and the other one to be having to be parameter. People want to deviate and now they're going to run the dishes. I surrender some nuances where to be very particular about what my default parameter set was, when I was doing complicated stuff like that, because having sliding parameters and strings being a positional one, lots of stuff can convert to a string. And there are some nuances in there, I remember I had to be very careful about my default parameter set and my positional ones. Yes. Are there a script analyzer rules to assist with that layout or is the PS code just in its mind? I suppose script analyzer doesn't complain too much with the exception of when the tester allows you to do a before all or before each script block and then you might define a variable and then in your it statements, you use that variable, script analyzer isn't aware of processing cross script block. We need to understand how stuff executes and analyzer just has no context for that. Otherwise, okay, so I suppose on that note, if it does throw issues with some things, you can suppress script analyzer rules, like there's an attribute as well to suppress specific stuff for your items. Like you mentioned, there's a style that DSL is completely different than the style for command. As you say that, I think I do have those littered in my module where I'm not quite all like the naming, right, the noun verb. I think I had to use an attribute to say, I'm not following that. Okay, you had to be statically excuse the rules. Yeah, I used that too. But in money, your code, you put all these attributes to shut up script analyzer. Maybe nice to be able to put all those into a script analyzer file and isolate all that mess. Yeah, if I had to do it again, I would have used those rules for script analyzers, say this project. This is one that just doesn't apply. So it seems like if you were working with hierarchical data, creating something with hierarchy, you could just easily pass in a hierarchical, you know, custom object or something from JSON based on JSON. But I guess the advantage of going with this DSL approach is that you're able to provide more detail, more, I guess, simplified validation of the user input. And user input is kind of the key there. Like if you're dynamically generating that hierarchical object, like via code, you know, maybe you don't necessarily need any DSL for that at all. Just programmably build that thing. But if you're in front of it kind of writing these things out, that's who the DSL scores is for the end user. So I actually worked with Kevin, my question earlier was a loaded question about whether or not you could run dynamic code inside those script blocks. So the main benefit of this approach rather than creating, for example, nested hash tables for your hierarchical structure is you get a much cleaner dynamic data driven structure generation with this method. I think you might be able to do a lot of the same stuff in like a hash table initializer, but personally I think it looks a lot less clean and I'm not certain. I found that this approach was much cleaner. I can't pick great examples off the top of my head. This really stands out in shines when you're going out to AD and getting machines in the OUs and generating dynamically where everything is in line rather than having to have a bunch of variables and then nesting a bunch of stuff that's now abstracted away in the variables. I think I need to wrap it up here. Please do the speaker evaluations for all of the speakers. This is actually my first time presenting here and I'd appreciate the feedback. Good or bad, I'll take it either way. Enjoy the rest of the conference.
 Alright, I guess we'll go and get started.  So today we're going to be talking about domain-specific languages and how to write  one in PowerShell.  My name is Kevin Marquette.  I'm actually going to cover quite a bit of material.  So some of the intro stuff, I might go through a little bit quick so we can actually get  to the good stuff kind of in the middle.  My slides and presentations are always up on GitHub.  If you want to download it for the code samples that are presented in this presentation, feel  free to do so.  So what is a DSL?  Right, a domain-specific language is used when you want to solve a problem in a specific  domain space.  Generally there's like terms and language around a certain problem domain that might  actually help you solve that problem better than a general language can.  So PowerShell is good as a jack of all trades.  It can do many things.  But sometimes you need a tool to do that one specific thing really, really well.  And that's kind of where DSL's kind of answer that problem.  We already have several DSL's in the PowerShell community.  Like if you've been working with DSC and Pester, like those are great examples.  I have several up here that I've seen in the community and I'm going to actually, we're  going to walk through a couple of these real quick just to see what a DSL looks like in  PowerShell.  Okay.  We all know and love DSC.  And as we look at this as a DSL, we actually get to describe our problems there.  Like you say, we're a configuration that's configuring a node that's setting up a Windows  feature and has some like file web content.  Like the language helps describe the problems that we're solving with it.  And it does a really great job of doing that.  Another DSL many of us are familiar with is Pester.  It uses beautiful syntax where we can describe what it should do and using that term in the  terminology, we actually focus in and write better tests using a syntax that is, that  rolls off the tongue a little bit better.  A couple more examples of DSL's is like invoke build.  So if you use invoke build or sake, right, it's like a build automation DSL.  It's built around if you have tasks that perform certain things in your build pipeline.  And there's certain terms and nomenclature around that effort that is captured really  well in both of those DSL's.  Example near and dear to my heart is PSGraph from a module that I wrote that is implemented  to describe a node edge-based relationship of things you want to diagram visually.  So this one here, I actually describe three nodes and I can describe how they relate to  each other and when you execute it, we can actually see the diagram based off exactly  how we described it.  And last example from the community is this plaster manifest.  Has anybody worked with plaster?  So for those of you that haven't, it's templates that you can use to generate functions or  modules or anything, anything really.  And right in the core of it is this manifest that's built in XML that describes what inputs  to receive from the user and what files to transform and deploy.  If you don't like working in it with XML, here is a community DSL that is specific to  defining those configurations.  This one here has the parameters and the content and if I take a quick peek at this, we should  get a large, it's actually generating this XML for me to use as part of plaster.  Like this is a great use case of a DSL.  And I wanted to run through these just to kind of get a feel of, they look kind of unique,  a little bit different from the PowerShell we're normally writing.  And so let's take it a little bit deeper.  Let's really deconstruct how some of these DSLs are doing the magic that they're doing.  Because when we look at Pester, it's really just a function.  That first command is just the command line like you would normally write.  The next description that we use is a positional parameter.  And then at the end, we've got a script block that hangs off the end.  Both of them are positionals.  So in a way, these are, this is PowerShell, but we're using it in a very different way  than you would in most use cases.  This pattern repeats through this DSL as well, where my, it does something, also has parameters  that are positional, a script block that is also positional.  And then our should statement is just another function that's taking pipeline input.  And when you break it down that way, it's easier to process what's actually happening  when you're running these DSLs.  So we tear Pester down a little bit more.  So I'm going to start with our same example here.  Can I get this?  So starting with our same example, if we look at the help, the syntax I've described,  we can see that it is exactly how I described it.  We've got a positional parameter name for our string.  Our fixture is the parameter name for the script block.  And as we take that knowledge, we can actually expand that Pester statement into like a fully,  the more verbose syntax that PowerShell tends to use.  And then here we see we have the name, our fixture, and we kind of repeat this, right?  So all these things actually do have name parameters, but in the style of a DSL, we  kind of don't use those.  Now if we keep going down this path, right, and tear this part even more, imagine if this  actually was more PowerShell-like, like more like the code you write that's more procedural.  If we start from the bottom, could you imagine if we actually write our test this way, like  if we write our described block and had to pass it a script block as a parameter, right?  Or then our fixture itself contained another statement that took a script block.  And I don't know about you, but I probably wouldn't have adopted Pester so fast if I  had to write every one of my tests exactly this way.  And as I start to see how a DSL can solve certain problems, I let you see the answers  in a different way.  Because when I compare this to something like this, I'm so glad that I can actually do my  Pester tests like this.  Now I'm sure if we had to do it the other way, we would have had different solutions  for the problem, but this is the problem that a DSL can address.  So we kind of covered a lot really quickly there.  There's a lot of great points when you just start deconstructing those DSLs in that way.  And the first is, like I said, DSL commands are just functions.  And we also don't follow community best practices.  It's really weird when we start writing a DSL because I've had years of, here's the  best practice to do something.  And I'm fighting all those for the right reason.  But it feels like I'm running a really dirty code when I know it's really clean and insolvent  problem.  The names, right, we don't use the name verb so often.  We name them specifically to match the problem domain that we're working in.  We're always using these unnamed parameters or positional parameters.  We'll often leave off the quotes in our syntax and style.  And I say we abuse script blocks.  I say it that way because you normally don't hang script blocks off the end of a function  unless you're doing it in a DSL this way.  I think I've gotten more used to that syntax.  I'll do like an invoke command now and I'll hang the script blocks.  But in the earlier days of DSLs, I don't think that was as common of a style.  There's some other hacks you can do by hanging an array off the end or a hash table.  I don't like it so much.  But if that solves a specific solution and that syntax makes a lot of sense, it's viable  to do that.  I've used it once.  I've kind of gotten away from it just because the best practice feels more like using a  script block there than any other hanging structure.  Then the other common pattern we see is a lot of DSLs have a invoke type function, like  this helper tool that consumes the DSL a bit and wraps it in more functionality.  Pester, you can run a describe block and you get that fancy output or the beautiful output  of your tests.  But you miss out on other features such as the number of tests that passed or failed  or status of failure.  They give you formatted XML output files, code coverage.  Quite often those helper functions take the DSLs and actually add more value to those.  Okay, so we've got a good picture in our mind of what the basics are and all the little  pieces.  If we're wanting to plan a DSL, the first question you're going to ask is make sure  a DSL is what you need.  Just because you can do something doesn't mean that you should.  I know I've gotten excited and really enjoy working with those and I probably used DSLs  way too much.  It solves a problem in some cases and because you break your community best practices, it  does confuse your audience unless they understand that domain that you're working in.  Prototype the syntax before you code it.  This is probably the most valuable tip that I've learned when writing and working with  DSLs is write the code as if you're using it.  Pretend like everything exists and get a feel for your interaction with it and do a couple  different versions of it.  See the syntax that you think you can implement and that fits really, really well with the  flow you want.  Yes?  Can you say that's a general best practice for writing scripts or modules?  We used to have teams write all their modules and then they'd come up all screwed up.  Then we adopted the practice we called demo.text.  Before you write anything, write off the demo.text.  Show us what somebody type, what they'll get, what the flows will be and then we review  that and that's where you find all your problems.  Absolutely.  You get such better consistency across stuff when you do the flow that way and save those  samples and put those in your pester tests.  What you guys are almost describing as well, if you come from the development world is  test-read and develop.  You write code using stuff that doesn't exist yet.  See you have your failing test to begin with.  You write your test so that you know what the output should be, which also fails because  you don't even have anything that outputs.  Then you write the code and at that point you are already documented both the input and  the output for what you're describing.  That's the middle step and the first step to demo.text is really just to focus in on  the user experience itself.  If you do the test, it's hard to see the experience.  I'll take those prototypes and when I'm done, those feed right into your test flow.  More like behavior if you're in development where you're testing what the user behavior  will be.  Okay.  I always blur those too when I refer to them.  Thank you.  Okay.  Let's actually build a DSL from the ground up.  For this one, if you've used RDC man desktop connection manager, it actually uses an XML  file to store all the information about your connections.  All right.  DSL planning.  So there's the middle of it.  Okay.  So first let's prototype the commands we would use.  I start with the PowerShell type syntax because maybe that's going to be my answer and it  might actually be a better solution in this case, but we're making a DSL here.  If I was going to do functions, I think this is kind of the flow I would come up with where  I can define here's the servers to create this file or maybe a group that I could put  the servers in.  That'd be a good set of command lists.  Let's think of this as a DSL.  So my idea number two is we do something a little bit of a structure to it.  I define my file with a group and maybe listen to the servers in it.  Here I'm using that hanging array.  I'm not really sold on that.  And the more I play with it, I don't feel like that's quite the right solution here.  So my third idea I came up with, and that's prototyping, was something more like this  where I describe a group, you know, I'm calling this one production, and that group contains  my server, my RDC server, server one and server two.  And what I like about this is that if I wanted to extend this later and add other properties  to this guy, knowing that he's just a function, we can add like say a describe or a function  or a description to add more data to that, to our DSL later.  Okay.  So this is the syntax I've decided on.  This is the one I commit to developing and planning for right here.  As we continue on, I'll kind of expand out all the possible properties just so I know  what I'm actually coding.  So I sat down and made up these names here off of my template, off of my little piece  of scratch code, and I'm now going to go implement those parameters.  Is that a big track with me so far?  Beautiful.  Okay.  Now, so the RDC man XML file, you can think of it as this large XML document.  Inside that, it contains groups.  That's another like XML structured, and that group contains XML servers, right?  So I've got this outer template to this inner group, to this inner object.  So the first one I'm going to write in this case is my RDC server.  And my parameters, based off of our spec, I'm just taking the computer name now.  This could be expanded to more parameters later.  So it's positional, mandatory.  I could take it from the pipeline, but that's another discussion.  All right.  So let's break down to our process looks like.  So all I'm really doing in this example is wrapping a value around injecting it into  some chunk of strings, which happens to be XML.  I definitely could have actually done the XML node object and made full objects on this,  but for the simplicity of this demo, this is what we want to do.  It's much easier to follow.  And all I'm really doing is creating a string and tossing it on the pipe for somebody else  to deal with.  I'll have another function that cares what's coming down to it.  So I literally just have a small chunk of valid XML for my server.  So I need to invoke this because we're actually going to run this here in a moment.  All right, let's get that loaded.  Then RDC group.  So here's where we're starting a little bit of the fun.  So I still have my group name as a positional parameter.  Then our second one is a nether positional parameter in the search group block.  I'm calling this one child item.  I could probably pick a better name for that.  And then I'm doing the same thing before.  Like I have a small chunk of XML, I inject my value, toss in the pipe, and then I actually  go to my script block and just say invoke the script block.  Whatever's inside the script block, we're going to run that because there's other commands  in there that need to execute and run.  And in this case, this command doesn't care what happens.  I'm just going to toss on the pipe as well.  So somebody else upstream is going to handle this.  And in a way, I've just got this giant string builder.  When you really break it down to it, I'm building just giant strings.  So let's load this guy up as well.  So now the outer object.  And I think he might be something fancy.  He's actually just as plain as our group.  There's just more XML overhead.  I'm going to invoke my script block because it's like what the collection is, the child  items, and more XML footer.  So at the end, all I've done is just compile this XML dead and toss it down the pipe.  But because I did my child items in the right way, I added the structure to it.  So if I look at, let me go back up to this guy slightly here.  Like you'll notice, I start a group, I have properties, and I know whatever I invoke in  here definitely needs to give me valid XML for this to hold, for this to work.  And I close my XML object.  So let's run this one as well and see if this comes together like we expect.  And that one.  OK.  So here is that first example we started with.  Here's our prototype.  When we take the next step, we'll save it to a file, and then we're actually going to  open it up with remote desktop connection manager.  So let's do this.  Summing up from loaded properly.  We execute that and actually created my RDC connection manager profile.  And that's where it gets cool is this matches the description of what I wanted to generate.  And that's where that DSL solved that problem really beautifully.  We're having all those commands to do it.  You can get that break from the pattern.  And because of the way I wrote it, let's try something a little bit more complicated.  Let's say we get a group more like this, where I'm defining, that's so tall.  So I have got a group that contains a group that contains servers.  Because the structure is valid the way we're doing our XML, I can actually, I can do this.  So when we run this command right here, we get exactly what we described in RDC manager.  So that's where DSLs get really cool is that's what I told it.  That's what I wanted to see.  And it aligns so perfectly with the output that I produced.  I suppose any questions at this point?  Well, following kind of what we built here?  Yes?  Just something I'm noticing that DSLs really sell in hierarchical data.  Absolutely.  And then we work in linear sort of like flat data.  You might not get as much of a benefit in that case.  But yeah, hierarchical stuff is also, that's why my jumps to DSLs way too fast.  But that's where I find the best solutions of stuff that has a hierarchy to it.  All right.  And then if we wanted an invoke command for this, it might be something where we save  those into like an rdg file.  And we could run a, you know, build rdcman where it finds all those files and saves it  to an output.  I don't know that's actually useful in this case for DSL, but for many of them you'll  see a pattern like that.  All right.  Time.  Perfect.  I can see a case where you're going to a client and you want to quickly get a console to  have a list of all of the servers in the OU and give you the, the got the way for the  patient.  Actually, that's a great interaction because you'll be with the client and they can be  telling you what, describing it to you and you'll be typing out in a way that they could  almost understand it.  And that's exactly it.  We're using script blocks, right?  And you can mix.  That's one of the biggest discoveries I had with Pester was it truly was just PowerShell  commands.  And I could wrap all the PowerShell around that that I wanted, you know, for each statement  or if logic, like I could decide to never run a test based off of the conditions.  Now Pester supports that with its parameters, but there's a lot of cool things you can do  knowing that it's just PowerShell.  So because those are script blocks, couldn't you also dynamically generate stuff nested  in the hierarchical structure?  Yes.  Yeah, like I said, that could.  Concretely, what you could do is you could set, like that engagement you mentioned where  you say a little bit of a lecture, or a little bit of a lecture.  And you have that written up and that interloop of the servers and set up hard wiring.  You could have a query out to AD to a specific OU and then generate.  You just have a for loop and boom, the power is just crazy.  Oh, you lost your stage, by the way.  Yep.  I'll see if it comes back up.  Okay.  So like this command, okay, I'm going off script here.  We'll see how this works.  I'll do one dot dot 10, and we'll say, oh, so I think.  I think that's what I want.  Maybe not.  Just put a comment saying before the last part.  That'll just give you strings.  He needs the RDC server.  Oh, I see.  Thank you.  I deleted the wrong thing.  I couldn't see it when I was staring at it.  That's what I wanted.  All right.  So this stuff can't be edited.  And mentioned earlier, I've added pipeline support to that DSL command because that's  my pattern.  And I'm going off-strip on that too, but anytime I can actually reliably add pipeline support  to build a process stuff like that, I get so much value out of it downstream.  I'm writing it for one thing, but if I can design it for anything, I'll use it for anything  later.  All right.  We lost.  Okay.  So that's my big demo.  If I do have some DSL tricks, I want to call them design patterns, but I don't know if  they're actually design patterns or not.  But we'll kind of work through that.  So sometimes I have a command for lack of a word, I'll call it like a value pass-through,  where I'll actually create a DSL command name that takes a string and just returns that  string because the syntax and flow for my user might make sense to actually use that  word.  A good example is my PSGraph.  It's really just a wrapper in front of graphViz, which actually has its own DSL.  And if you're an expert in graphViz and know the syntax for something that I don't support  or maybe I do, I have an inline command, we say inline, and put the inlineDSL for graphViz.  But under the hoods, you could have just put a string there and it would have worked anyways.  So for the sake of my DSL, I added a command for inline, I put documentation and tell people  how to use it.  It's easier for them to discover than trying to tell them you could put random strings in  your strip loss and this stuff would just work.  So there's value in designing for the user.  The next two, which I'll refer to as like the simple template and the nested template,  is exactly what we wrote with this RDC man.  The first one is just a value shoved into something that's wrapping it.  We're using strings in this case, but it could be classes or objects or hash tables where  you're getting the values shoving it into something and passing it up the string.  Then what I mean by a nested template is one that does the same thing but executes the  script block.  And that's what my RDC group was, was something with like a nested templating pattern.  Now some other tricks I've used in DSLs is like a, I'll call it a hash table pass through  where I'll take parameters so they can write something like this.  Like for like, what?  Oh, changed again.  You know what?  PowerPoint has been playing games with me up here.  Okay.  So I could have a command like this where I say a state machine where I'm saying state  started with a script block and upstream I just want a hash table and I found, I can  actually just take all the properties and the parameters and just return the psbrown  parameters up the stack for somebody to deal with as a quick way to create hash tables  that are structured with the syntax that people can discover.  Then that same note, I've used this pattern where I want a syntax that looks just like  this which is just like DSC but I'm not using the dynamic keyword to do this.  And I'll come back to that special approach later if I have time.  But there's an alternate way to do that just in PowerShell where I build my script block  and actually rebuild it in line as an ordered hash table.  So I'm making some assumptions on my users that they'll use that syntax but if I want  this to come out as a hash table through my pipeline or however I'm going to use it, I  can literally say, you know what, let's just take my script block, make it a string, wrap  it in order and execute it and return that object back.  The value in that is that you can get that object the way you want to see it.  So if I run this here and actually execute my sample up above, he's going to give me  a hash table that looks, so I got a syntax that I want.  I can get the data I want somewhere else.  If I keep going down the list here, like an object collector, I mean, so far I've done  a lot of taking objects and just tossing up the pipeline and not caring about it.  And for string builders, sometimes your output is just everything tossed on the pipeline.  There are other times though, I say if I'm building a state machine, we actually want  to collect all those objects and do something with them.  And it's actually as straightforward as you'd assume where you run your script block and  you've executed it and you save it to a variable.  So you have a variable full of objects that came down through your DSL and then you walk  them and process them.  If they're mixed objects, you want to do your is your types, but you want to walk the list  and account for what's coming up the pipeline.  So when I say somebody's going to handle that later, it might be this pattern that's handling  it later to decide what to do with those.  In this example, I built a state machine.  I don't know if you actually demo this really well, but my DSL up above, this guy gives  me hash tables with a state and a script block.  And then I actually, my state machine builder, I collect those in a hash table based off  the key is the state and the action is the actual hash table object that can be processed  through a state machine.  Then other fun stuff.  All right.  So maybe you want not any command to run, right?  Like I'm using script blocks.  People could put anything they want in that script block, any commands.  And if you have a DSL where you're writing it to disk and you're making certain assumptions  about what those files are, you can actually restrict what actually gets executed.  I think initially with prior partial one, we had the data statement, early partial with  this data statement, that anything in there was treated as data and could basically whitelist  the commands to execute.  I think more recently, you could also use the check restricted language feature of a  script block to say the only commands, command lists or functions to run should be, you know,  these commands that I'm defining right here.  But now you can actually place control over what could arbitrarily be in that script block.  Now you break the ability to do, like, you get AD, you know, computer to pipe into a  server list, but you do gain trust in what's in those script blocks.  The internal only commands.  So maybe you don't want to leak all of your commands out into the command space where  you want them to actually execute in a certain structure.  One way to do that is actually if you define your DSL commands inside of another command,  the scope holds it kind of in place.  It just won't exist, or the outside of that.  You lose your get help and discoverability on those commands doing tricks like that.  Another thing you can do, a little bit expensive, they could check the PS call stack to see  am I being called as a child element of, like, say, my RDC manager.  Like, RDC group on his own has no value.  So should he even run if he's not part of the larger structure?  Is that how the tester makes sure that your is inside of a described or complex one?  Probably.  I don't actually know, but if I was going to make the assumption and implement myself,  I'd probably check the call stack first.  But that's all my list to, like, dive down and see exactly their approach.  The dynamic keyword.  Okay.  So this is kind of like classes in PowerShell in that it was implemented specifically for  DSC to be able to do what it does well.  And it's kind of fuzzy when you use it for anything else.  What the dynamic keyword does is it takes your commandlet and basically allows the AST  to treat it as a command.  And we've been using all these dangling strip blocks.  Being promoted to a true command in the PowerShell AST allows that strip block to not hang  anymore.  So just like your if statement, for I think the first to be on the end or on the next  line, creating it with the dynamic keyword would allow you to make it look more like  a native PowerShell command.  And you still have the option to have a strip block in there or use, like, a hash table.  Like the DSC resource would be like a hash table dynamic keyword.  And then you get things like autocomplete and syntax validation.  You can define these are the keys that are only, that are possible in there.  And the AST will tell you, will parse that and throw errors if something is used that  you didn't define.  So we have a nice tight structure.  The dynamic keyword can kind of help you with that.  But there are gotchas.  And the first one is that it has to kind of exist before the script is parsed.  So if you have a script where you're using it and you try to dot source your dynamic  keyword logic into it, it's going to fail because it's going to parse your script first  before it goes to the dot source.  Very similar with classes.  You kind of run into those same nuances.  And you also can't currently use other function parameters.  Like if you're working with Pester, you can do a describe and give it tags and have additional  parameters on there or you can skip a test or it's a pending test.  In my experimentation with dynamic keyword, I can't add, I can't also use those parameters.  You kind of give up that.  I think there's a third limitation because of the way dynamic keywords are implemented  and that is that your variable scoping starts to have strange behaviors because the way  this works is that it just takes a copy of ASD and sort of pours it into, it turns into  a function for later on.  I did some work.  I'm on the power siding.  Okay.  Thank you.  I'm going to give you the same word.  Prototyping first class DSL support in PowerShell a year ago and took months for us to realize  that there was another limitation here was in variable scoping because of the whole dynamic  scope.  Okay.  Thank you.  Now.  I can't remember the specific type of word.  I'm pointing this out for awareness.  I don't actually use this anywhere because of limitations.  As you're diving deep into DSLs, this is a thing to be aware of.  And then the last detail is actually learn this one this week is if you add the attributes  debugger step through, when somebody is debugging their scripts that uses your DSL, they'll  actually jump from their code to their code in the script block.  And they won't actually have to debug automatically through your DSL commands to get there.  This affects automatic breakpoints.  If you had a breakpoint on any time somebody says, you know, write outputs globally, your  functions will actually be excluded from those global breakpoint type scenarios.  All right.  So I suppose any questions or things we want to touch on anymore?  Yes.  What are the pain points and caveats that you've seen defining DSLs this way?  So the caveats are sometimes, well, sometimes I write really, it's like knowing best practices  and doing it for so long.  I feel like I'm writing a really ugly power show that's breaking a lot of best practices.  That's more of a procedural process thing.  I made, in my DSL, I made heavy use of positional parameters where I describe something that's  a string, like I describe something that has a string like a node.  And I also have like a hash table that is optional.  Like I still had like sliding parameters.  Actually, my subgraph would be the better example.  I could have a string, a positional hash table, and a positional script block.  But I made the hash table optional and the string optional.  So I have like six parameter sets to allow for this weird nuance of the user can choose  a script block or the hash table and the script.  And that made sense in the context of that DSL.  Yeah.  We're relying on those visual parameters.  We're kind of enforcing a sort of structure to the DSL, especially if you have two parameters  that are script blocks and make the future loss of the task.  We're expecting one script block to be hanging and the other one to be having to be parameter.  People want to deviate and now they're going to run the dishes.  I surrender some nuances where to be very particular about what my default parameter  set was, when I was doing complicated stuff like that, because having sliding parameters  and strings being a positional one, lots of stuff can convert to a string.  And there are some nuances in there, I remember I had to be very careful about my default  parameter set and my positional ones.  Yes.  Are there a script analyzer rules to assist with that layout or is the PS code just in  its mind?  I suppose script analyzer doesn't complain too much with the exception of when the tester  allows you to do a before all or before each script block and then you might define a variable  and then in your it statements, you use that variable, script analyzer isn't aware of processing  cross script block.  We need to understand how stuff executes and analyzer just has no context for that.  Otherwise, okay, so I suppose on that note, if it does throw issues with some things,  you can suppress script analyzer rules, like there's an attribute as well to suppress  specific stuff for your items.  Like you mentioned, there's a style that DSL is completely different than the style for  command.  As you say that, I think I do have those littered in my module where I'm not quite all like  the naming, right, the noun verb.  I think I had to use an attribute to say, I'm not following that.  Okay, you had to be statically excuse the rules.  Yeah, I used that too.  But in money, your code, you put all these attributes to shut up script analyzer.  Maybe nice to be able to put all those into a script analyzer file and isolate all that  mess.  Yeah, if I had to do it again, I would have used those rules for script analyzers, say  this project.  This is one that just doesn't apply.  So it seems like if you were working with hierarchical data, creating something with  hierarchy, you could just easily pass in a hierarchical, you know, custom object or something  from JSON based on JSON.  But I guess the advantage of going with this DSL approach is that you're able to provide  more detail, more, I guess, simplified validation of the user input.  And user input is kind of the key there.  Like if you're dynamically generating that hierarchical object, like via code, you know,  maybe you don't necessarily need any DSL for that at all.  Just programmably build that thing.  But if you're in front of it kind of writing these things out, that's who the DSL scores  is for the end user.  So I actually worked with Kevin, my question earlier was a loaded question about whether  or not you could run dynamic code inside those script blocks.  So the main benefit of this approach rather than creating, for example, nested hash tables  for your hierarchical structure is you get a much cleaner dynamic data driven structure  generation with this method.  I think you might be able to do a lot of the same stuff in like a hash table initializer,  but personally I think it looks a lot less clean and I'm not certain.  I found that this approach was much cleaner.  I can't pick great examples off the top of my head.  This really stands out in shines when you're going out to AD and getting machines in the OUs  and generating dynamically where everything is in line rather than having to have a bunch of variables  and then nesting a bunch of stuff that's now abstracted away in the variables.  I think I need to wrap it up here.  Please do the speaker evaluations for all of the speakers.  This is actually my first time presenting here and I'd appreciate the feedback.  Good or bad, I'll take it either way.  Enjoy the rest of the conference.
Writing_a_DSL_0_47.png Writing_a_DSL_48_131.png Writing_a_DSL_132_227.png Writing_a_DSL_228_491.png Writing_a_DSL_492_995.png Writing_a_DSL_996_1799.png Writing_a_DSL_1800_2411.png Writing_a_DSL_2412_2459.png Writing_a_DSL_2460_3899.png Writing_a_DSL_3900_4547.png Writing_a_DSL_4548_4667.png Writing_a_DSL_4668_4715.png Writing_a_DSL_4716_5183.png Writing_a_DSL_5184_5363.png Writing_a_DSL_5364_5507.png Writing_a_DSL_5508_5855.png Writing_a_DSL_5856_6491.png Writing_a_DSL_6492_7379.png Writing_a_DSL_7380_7499.png Writing_a_DSL_7500_9143.png Writing_a_DSL_9144_9203.png Writing_a_DSL_9204_10751.png Writing_a_DSL_10752_11039.png Writing_a_DSL_11040_12455.png Writing_a_DSL_12456_21035.png Writing_a_DSL_21036_23243.png Writing_a_DSL_23244_25103.png Writing_a_DSL_25104_25583.png Writing_a_DSL_25584_29231.png Writing_a_DSL_29232_30671.png Writing_a_DSL_30672_30899.png Writing_a_DSL_30900_31283.png Writing_a_DSL_31284_31451.png Writing_a_DSL_31452_32375.png Writing_a_DSL_32376_33611.png Writing_a_DSL_33612_34211.png Writing_a_DSL_34212_34307.png Writing_a_DSL_34308_36287.png Writing_a_DSL_36288_36923.png Writing_a_DSL_36924_36995.png Writing_a_DSL_36996_37067.png Writing_a_DSL_37068_39455.png Writing_a_DSL_39456_39515.png Writing_a_DSL_39516_39743.png Writing_a_DSL_39744_39983.png Writing_a_DSL_39984_43487.png Writing_a_DSL_43488_43535.png Writing_a_DSL_43536_43679.png Writing_a_DSL_43680_49079.png Writing_a_DSL_49080_49187.png Writing_a_DSL_49188_49331.png Writing_a_DSL_49332_49403.png Writing_a_DSL_49404_49799.png Writing_a_DSL_49800_53423.png Writing_a_DSL_53424_59279.png Writing_a_DSL_59280_68555.png Writing_a_DSL_68556_68651.png Writing_a_DSL_68652_68699.png Writing_a_DSL_68700_68740.png
Alright, I guess we'll go and get started. So today we're going to be talking about domain-specific languages and how to write one in PowerShell. My name is Kevin Marquette. I'm actually going to cover quite a bit of material. So some of the intro stuff, I might go through a little bit quick so we can actually get to the good stuff kind of in the middle. My slides and presentations are always up on GitHub. If you want to download it for the code samples that are presented in this presentation, feel free to do so. So what is a DSL? Right, a domain-specific language is used when you want to solve a problem in a specific domain space. Generally there's like terms and language around a certain problem domain that might actually help you solve that problem better than a general language can. So PowerShell is good as a jack of all trades. It can do many things. But sometimes you need a tool to do that one specific thing really, really well. And that's kind of where DSL's kind of answer that problem. We already have several DSL's in the PowerShell community. Like if you've been working with DSC and Pester, like those are great examples. I have several up here that I've seen in the community and I'm going to actually, we're going to walk through a couple of these real quick just to see what a DSL looks like in PowerShell. Okay. We all know and love DSC. And as we look at this as a DSL, we actually get to describe our problems there. Like you say, we're a configuration that's configuring a node that's setting up a Windows feature and has some like file web content. Like the language helps describe the problems that we're solving with it. And it does a really great job of doing that. Another DSL many of us are familiar with is Pester. It uses beautiful syntax where we can describe what it should do and using that term in the terminology, we actually focus in and write better tests using a syntax that is, that rolls off the tongue a little bit better. A couple more examples of DSL's is like invoke build. So if you use invoke build or sake, right, it's like a build automation DSL. It's built around if you have tasks that perform certain things in your build pipeline. And there's certain terms and nomenclature around that effort that is captured really well in both of those DSL's. Example near and dear to my heart is PSGraph from a module that I wrote that is implemented to describe a node edge-based relationship of things you want to diagram visually. So this one here, I actually describe three nodes and I can describe how they relate to each other and when you execute it, we can actually see the diagram based off exactly how we described it. And last example from the community is this plaster manifest. Has anybody worked with plaster? So for those of you that haven't, it's templates that you can use to generate functions or modules or anything, anything really. And right in the core of it is this manifest that's built in XML that describes what inputs to receive from the user and what files to transform and deploy. If you don't like working in it with XML, here is a community DSL that is specific to defining those configurations. This one here has the parameters and the content and if I take a quick peek at this, we should get a large, it's actually generating this XML for me to use as part of plaster. Like this is a great use case of a DSL. And I wanted to run through these just to kind of get a feel of, they look kind of unique, a little bit different from the PowerShell we're normally writing. And so let's take it a little bit deeper. Let's really deconstruct how some of these DSLs are doing the magic that they're doing. Because when we look at Pester, it's really just a function. That first command is just the command line like you would normally write. The next description that we use is a positional parameter. And then at the end, we've got a script block that hangs off the end. Both of them are positionals. So in a way, these are, this is PowerShell, but we're using it in a very different way than you would in most use cases. This pattern repeats through this DSL as well, where my, it does something, also has parameters that are positional, a script block that is also positional. And then our should statement is just another function that's taking pipeline input. And when you break it down that way, it's easier to process what's actually happening when you're running these DSLs. So we tear Pester down a little bit more. So I'm going to start with our same example here. Can I get this? So starting with our same example, if we look at the help, the syntax I've described, we can see that it is exactly how I described it. We've got a positional parameter name for our string. Our fixture is the parameter name for the script block. And as we take that knowledge, we can actually expand that Pester statement into like a fully, the more verbose syntax that PowerShell tends to use. And then here we see we have the name, our fixture, and we kind of repeat this, right? So all these things actually do have name parameters, but in the style of a DSL, we kind of don't use those. Now if we keep going down this path, right, and tear this part even more, imagine if this actually was more PowerShell-like, like more like the code you write that's more procedural. If we start from the bottom, could you imagine if we actually write our test this way, like if we write our described block and had to pass it a script block as a parameter, right? Or then our fixture itself contained another statement that took a script block. And I don't know about you, but I probably wouldn't have adopted Pester so fast if I had to write every one of my tests exactly this way. And as I start to see how a DSL can solve certain problems, I let you see the answers in a different way. Because when I compare this to something like this, I'm so glad that I can actually do my Pester tests like this. Now I'm sure if we had to do it the other way, we would have had different solutions for the problem, but this is the problem that a DSL can address. So we kind of covered a lot really quickly there. There's a lot of great points when you just start deconstructing those DSLs in that way. And the first is, like I said, DSL commands are just functions. And we also don't follow community best practices. It's really weird when we start writing a DSL because I've had years of, here's the best practice to do something. And I'm fighting all those for the right reason. But it feels like I'm running a really dirty code when I know it's really clean and insolvent problem. The names, right, we don't use the name verb so often. We name them specifically to match the problem domain that we're working in. We're always using these unnamed parameters or positional parameters. We'll often leave off the quotes in our syntax and style. And I say we abuse script blocks. I say it that way because you normally don't hang script blocks off the end of a function unless you're doing it in a DSL this way. I think I've gotten more used to that syntax. I'll do like an invoke command now and I'll hang the script blocks. But in the earlier days of DSLs, I don't think that was as common of a style. There's some other hacks you can do by hanging an array off the end or a hash table. I don't like it so much. But if that solves a specific solution and that syntax makes a lot of sense, it's viable to do that. I've used it once. I've kind of gotten away from it just because the best practice feels more like using a script block there than any other hanging structure. Then the other common pattern we see is a lot of DSLs have a invoke type function, like this helper tool that consumes the DSL a bit and wraps it in more functionality. Pester, you can run a describe block and you get that fancy output or the beautiful output of your tests. But you miss out on other features such as the number of tests that passed or failed or status of failure. They give you formatted XML output files, code coverage. Quite often those helper functions take the DSLs and actually add more value to those. Okay, so we've got a good picture in our mind of what the basics are and all the little pieces. If we're wanting to plan a DSL, the first question you're going to ask is make sure a DSL is what you need. Just because you can do something doesn't mean that you should. I know I've gotten excited and really enjoy working with those and I probably used DSLs way too much. It solves a problem in some cases and because you break your community best practices, it does confuse your audience unless they understand that domain that you're working in. Prototype the syntax before you code it. This is probably the most valuable tip that I've learned when writing and working with DSLs is write the code as if you're using it. Pretend like everything exists and get a feel for your interaction with it and do a couple different versions of it. See the syntax that you think you can implement and that fits really, really well with the flow you want. Yes? Can you say that's a general best practice for writing scripts or modules? We used to have teams write all their modules and then they'd come up all screwed up. Then we adopted the practice we called demo.text. Before you write anything, write off the demo.text. Show us what somebody type, what they'll get, what the flows will be and then we review that and that's where you find all your problems. Absolutely. You get such better consistency across stuff when you do the flow that way and save those samples and put those in your pester tests. What you guys are almost describing as well, if you come from the development world is test-read and develop. You write code using stuff that doesn't exist yet. See you have your failing test to begin with. You write your test so that you know what the output should be, which also fails because you don't even have anything that outputs. Then you write the code and at that point you are already documented both the input and the output for what you're describing. That's the middle step and the first step to demo.text is really just to focus in on the user experience itself. If you do the test, it's hard to see the experience. I'll take those prototypes and when I'm done, those feed right into your test flow. More like behavior if you're in development where you're testing what the user behavior will be. Okay. I always blur those too when I refer to them. Thank you. Okay. Let's actually build a DSL from the ground up. For this one, if you've used RDC man desktop connection manager, it actually uses an XML file to store all the information about your connections. All right. DSL planning. So there's the middle of it. Okay. So first let's prototype the commands we would use. I start with the PowerShell type syntax because maybe that's going to be my answer and it might actually be a better solution in this case, but we're making a DSL here. If I was going to do functions, I think this is kind of the flow I would come up with where I can define here's the servers to create this file or maybe a group that I could put the servers in. That'd be a good set of command lists. Let's think of this as a DSL. So my idea number two is we do something a little bit of a structure to it. I define my file with a group and maybe listen to the servers in it. Here I'm using that hanging array. I'm not really sold on that. And the more I play with it, I don't feel like that's quite the right solution here. So my third idea I came up with, and that's prototyping, was something more like this where I describe a group, you know, I'm calling this one production, and that group contains my server, my RDC server, server one and server two. And what I like about this is that if I wanted to extend this later and add other properties to this guy, knowing that he's just a function, we can add like say a describe or a function or a description to add more data to that, to our DSL later. Okay. So this is the syntax I've decided on. This is the one I commit to developing and planning for right here. As we continue on, I'll kind of expand out all the possible properties just so I know what I'm actually coding. So I sat down and made up these names here off of my template, off of my little piece of scratch code, and I'm now going to go implement those parameters. Is that a big track with me so far? Beautiful. Okay. Now, so the RDC man XML file, you can think of it as this large XML document. Inside that, it contains groups. That's another like XML structured, and that group contains XML servers, right? So I've got this outer template to this inner group, to this inner object. So the first one I'm going to write in this case is my RDC server. And my parameters, based off of our spec, I'm just taking the computer name now. This could be expanded to more parameters later. So it's positional, mandatory. I could take it from the pipeline, but that's another discussion. All right. So let's break down to our process looks like. So all I'm really doing in this example is wrapping a value around injecting it into some chunk of strings, which happens to be XML. I definitely could have actually done the XML node object and made full objects on this, but for the simplicity of this demo, this is what we want to do. It's much easier to follow. And all I'm really doing is creating a string and tossing it on the pipe for somebody else to deal with. I'll have another function that cares what's coming down to it. So I literally just have a small chunk of valid XML for my server. So I need to invoke this because we're actually going to run this here in a moment. All right, let's get that loaded. Then RDC group. So here's where we're starting a little bit of the fun. So I still have my group name as a positional parameter. Then our second one is a nether positional parameter in the search group block. I'm calling this one child item. I could probably pick a better name for that. And then I'm doing the same thing before. Like I have a small chunk of XML, I inject my value, toss in the pipe, and then I actually go to my script block and just say invoke the script block. Whatever's inside the script block, we're going to run that because there's other commands in there that need to execute and run. And in this case, this command doesn't care what happens. I'm just going to toss on the pipe as well. So somebody else upstream is going to handle this. And in a way, I've just got this giant string builder. When you really break it down to it, I'm building just giant strings. So let's load this guy up as well. So now the outer object. And I think he might be something fancy. He's actually just as plain as our group. There's just more XML overhead. I'm going to invoke my script block because it's like what the collection is, the child items, and more XML footer. So at the end, all I've done is just compile this XML dead and toss it down the pipe. But because I did my child items in the right way, I added the structure to it. So if I look at, let me go back up to this guy slightly here. Like you'll notice, I start a group, I have properties, and I know whatever I invoke in here definitely needs to give me valid XML for this to hold, for this to work. And I close my XML object. So let's run this one as well and see if this comes together like we expect. And that one. OK. So here is that first example we started with. Here's our prototype. When we take the next step, we'll save it to a file, and then we're actually going to open it up with remote desktop connection manager. So let's do this. Summing up from loaded properly. We execute that and actually created my RDC connection manager profile. And that's where it gets cool is this matches the description of what I wanted to generate. And that's where that DSL solved that problem really beautifully. We're having all those commands to do it. You can get that break from the pattern. And because of the way I wrote it, let's try something a little bit more complicated. Let's say we get a group more like this, where I'm defining, that's so tall. So I have got a group that contains a group that contains servers. Because the structure is valid the way we're doing our XML, I can actually, I can do this. So when we run this command right here, we get exactly what we described in RDC manager. So that's where DSLs get really cool is that's what I told it. That's what I wanted to see. And it aligns so perfectly with the output that I produced. I suppose any questions at this point? Well, following kind of what we built here? Yes? Just something I'm noticing that DSLs really sell in hierarchical data. Absolutely. And then we work in linear sort of like flat data. You might not get as much of a benefit in that case. But yeah, hierarchical stuff is also, that's why my jumps to DSLs way too fast. But that's where I find the best solutions of stuff that has a hierarchy to it. All right. And then if we wanted an invoke command for this, it might be something where we save those into like an rdg file. And we could run a, you know, build rdcman where it finds all those files and saves it to an output. I don't know that's actually useful in this case for DSL, but for many of them you'll see a pattern like that. All right. Time. Perfect. I can see a case where you're going to a client and you want to quickly get a console to have a list of all of the servers in the OU and give you the, the got the way for the patient. Actually, that's a great interaction because you'll be with the client and they can be telling you what, describing it to you and you'll be typing out in a way that they could almost understand it. And that's exactly it. We're using script blocks, right? And you can mix. That's one of the biggest discoveries I had with Pester was it truly was just PowerShell commands. And I could wrap all the PowerShell around that that I wanted, you know, for each statement or if logic, like I could decide to never run a test based off of the conditions. Now Pester supports that with its parameters, but there's a lot of cool things you can do knowing that it's just PowerShell. So because those are script blocks, couldn't you also dynamically generate stuff nested in the hierarchical structure? Yes. Yeah, like I said, that could. Concretely, what you could do is you could set, like that engagement you mentioned where you say a little bit of a lecture, or a little bit of a lecture. And you have that written up and that interloop of the servers and set up hard wiring. You could have a query out to AD to a specific OU and then generate. You just have a for loop and boom, the power is just crazy. Oh, you lost your stage, by the way. Yep. I'll see if it comes back up. Okay. So like this command, okay, I'm going off script here. We'll see how this works. I'll do one dot dot 10, and we'll say, oh, so I think. I think that's what I want. Maybe not. Just put a comment saying before the last part. That'll just give you strings. He needs the RDC server. Oh, I see. Thank you. I deleted the wrong thing. I couldn't see it when I was staring at it. That's what I wanted. All right. So this stuff can't be edited. And mentioned earlier, I've added pipeline support to that DSL command because that's my pattern. And I'm going off-strip on that too, but anytime I can actually reliably add pipeline support to build a process stuff like that, I get so much value out of it downstream. I'm writing it for one thing, but if I can design it for anything, I'll use it for anything later. All right. We lost. Okay. So that's my big demo. If I do have some DSL tricks, I want to call them design patterns, but I don't know if they're actually design patterns or not. But we'll kind of work through that. So sometimes I have a command for lack of a word, I'll call it like a value pass-through, where I'll actually create a DSL command name that takes a string and just returns that string because the syntax and flow for my user might make sense to actually use that word. A good example is my PSGraph. It's really just a wrapper in front of graphViz, which actually has its own DSL. And if you're an expert in graphViz and know the syntax for something that I don't support or maybe I do, I have an inline command, we say inline, and put the inlineDSL for graphViz. But under the hoods, you could have just put a string there and it would have worked anyways. So for the sake of my DSL, I added a command for inline, I put documentation and tell people how to use it. It's easier for them to discover than trying to tell them you could put random strings in your strip loss and this stuff would just work. So there's value in designing for the user. The next two, which I'll refer to as like the simple template and the nested template, is exactly what we wrote with this RDC man. The first one is just a value shoved into something that's wrapping it. We're using strings in this case, but it could be classes or objects or hash tables where you're getting the values shoving it into something and passing it up the string. Then what I mean by a nested template is one that does the same thing but executes the script block. And that's what my RDC group was, was something with like a nested templating pattern. Now some other tricks I've used in DSLs is like a, I'll call it a hash table pass through where I'll take parameters so they can write something like this. Like for like, what? Oh, changed again. You know what? PowerPoint has been playing games with me up here. Okay. So I could have a command like this where I say a state machine where I'm saying state started with a script block and upstream I just want a hash table and I found, I can actually just take all the properties and the parameters and just return the psbrown parameters up the stack for somebody to deal with as a quick way to create hash tables that are structured with the syntax that people can discover. Then that same note, I've used this pattern where I want a syntax that looks just like this which is just like DSC but I'm not using the dynamic keyword to do this. And I'll come back to that special approach later if I have time. But there's an alternate way to do that just in PowerShell where I build my script block and actually rebuild it in line as an ordered hash table. So I'm making some assumptions on my users that they'll use that syntax but if I want this to come out as a hash table through my pipeline or however I'm going to use it, I can literally say, you know what, let's just take my script block, make it a string, wrap it in order and execute it and return that object back. The value in that is that you can get that object the way you want to see it. So if I run this here and actually execute my sample up above, he's going to give me a hash table that looks, so I got a syntax that I want. I can get the data I want somewhere else. If I keep going down the list here, like an object collector, I mean, so far I've done a lot of taking objects and just tossing up the pipeline and not caring about it. And for string builders, sometimes your output is just everything tossed on the pipeline. There are other times though, I say if I'm building a state machine, we actually want to collect all those objects and do something with them. And it's actually as straightforward as you'd assume where you run your script block and you've executed it and you save it to a variable. So you have a variable full of objects that came down through your DSL and then you walk them and process them. If they're mixed objects, you want to do your is your types, but you want to walk the list and account for what's coming up the pipeline. So when I say somebody's going to handle that later, it might be this pattern that's handling it later to decide what to do with those. In this example, I built a state machine. I don't know if you actually demo this really well, but my DSL up above, this guy gives me hash tables with a state and a script block. And then I actually, my state machine builder, I collect those in a hash table based off the key is the state and the action is the actual hash table object that can be processed through a state machine. Then other fun stuff. All right. So maybe you want not any command to run, right? Like I'm using script blocks. People could put anything they want in that script block, any commands. And if you have a DSL where you're writing it to disk and you're making certain assumptions about what those files are, you can actually restrict what actually gets executed. I think initially with prior partial one, we had the data statement, early partial with this data statement, that anything in there was treated as data and could basically whitelist the commands to execute. I think more recently, you could also use the check restricted language feature of a script block to say the only commands, command lists or functions to run should be, you know, these commands that I'm defining right here. But now you can actually place control over what could arbitrarily be in that script block. Now you break the ability to do, like, you get AD, you know, computer to pipe into a server list, but you do gain trust in what's in those script blocks. The internal only commands. So maybe you don't want to leak all of your commands out into the command space where you want them to actually execute in a certain structure. One way to do that is actually if you define your DSL commands inside of another command, the scope holds it kind of in place. It just won't exist, or the outside of that. You lose your get help and discoverability on those commands doing tricks like that. Another thing you can do, a little bit expensive, they could check the PS call stack to see am I being called as a child element of, like, say, my RDC manager. Like, RDC group on his own has no value. So should he even run if he's not part of the larger structure? Is that how the tester makes sure that your is inside of a described or complex one? Probably. I don't actually know, but if I was going to make the assumption and implement myself, I'd probably check the call stack first. But that's all my list to, like, dive down and see exactly their approach. The dynamic keyword. Okay. So this is kind of like classes in PowerShell in that it was implemented specifically for DSC to be able to do what it does well. And it's kind of fuzzy when you use it for anything else. What the dynamic keyword does is it takes your commandlet and basically allows the AST to treat it as a command. And we've been using all these dangling strip blocks. Being promoted to a true command in the PowerShell AST allows that strip block to not hang anymore. So just like your if statement, for I think the first to be on the end or on the next line, creating it with the dynamic keyword would allow you to make it look more like a native PowerShell command. And you still have the option to have a strip block in there or use, like, a hash table. Like the DSC resource would be like a hash table dynamic keyword. And then you get things like autocomplete and syntax validation. You can define these are the keys that are only, that are possible in there. And the AST will tell you, will parse that and throw errors if something is used that you didn't define. So we have a nice tight structure. The dynamic keyword can kind of help you with that. But there are gotchas. And the first one is that it has to kind of exist before the script is parsed. So if you have a script where you're using it and you try to dot source your dynamic keyword logic into it, it's going to fail because it's going to parse your script first before it goes to the dot source. Very similar with classes. You kind of run into those same nuances. And you also can't currently use other function parameters. Like if you're working with Pester, you can do a describe and give it tags and have additional parameters on there or you can skip a test or it's a pending test. In my experimentation with dynamic keyword, I can't add, I can't also use those parameters. You kind of give up that. I think there's a third limitation because of the way dynamic keywords are implemented and that is that your variable scoping starts to have strange behaviors because the way this works is that it just takes a copy of ASD and sort of pours it into, it turns into a function for later on. I did some work. I'm on the power siding. Okay. Thank you. I'm going to give you the same word. Prototyping first class DSL support in PowerShell a year ago and took months for us to realize that there was another limitation here was in variable scoping because of the whole dynamic scope. Okay. Thank you. Now. I can't remember the specific type of word. I'm pointing this out for awareness. I don't actually use this anywhere because of limitations. As you're diving deep into DSLs, this is a thing to be aware of. And then the last detail is actually learn this one this week is if you add the attributes debugger step through, when somebody is debugging their scripts that uses your DSL, they'll actually jump from their code to their code in the script block. And they won't actually have to debug automatically through your DSL commands to get there. This affects automatic breakpoints. If you had a breakpoint on any time somebody says, you know, write outputs globally, your functions will actually be excluded from those global breakpoint type scenarios. All right. So I suppose any questions or things we want to touch on anymore? Yes. What are the pain points and caveats that you've seen defining DSLs this way? So the caveats are sometimes, well, sometimes I write really, it's like knowing best practices and doing it for so long. I feel like I'm writing a really ugly power show that's breaking a lot of best practices. That's more of a procedural process thing. I made, in my DSL, I made heavy use of positional parameters where I describe something that's a string, like I describe something that has a string like a node. And I also have like a hash table that is optional. Like I still had like sliding parameters. Actually, my subgraph would be the better example. I could have a string, a positional hash table, and a positional script block. But I made the hash table optional and the string optional. So I have like six parameter sets to allow for this weird nuance of the user can choose a script block or the hash table and the script. And that made sense in the context of that DSL. Yeah. We're relying on those visual parameters. We're kind of enforcing a sort of structure to the DSL, especially if you have two parameters that are script blocks and make the future loss of the task. We're expecting one script block to be hanging and the other one to be having to be parameter. People want to deviate and now they're going to run the dishes. I surrender some nuances where to be very particular about what my default parameter set was, when I was doing complicated stuff like that, because having sliding parameters and strings being a positional one, lots of stuff can convert to a string. And there are some nuances in there, I remember I had to be very careful about my default parameter set and my positional ones. Yes. Are there a script analyzer rules to assist with that layout or is the PS code just in its mind? I suppose script analyzer doesn't complain too much with the exception of when the tester allows you to do a before all or before each script block and then you might define a variable and then in your it statements, you use that variable, script analyzer isn't aware of processing cross script block. We need to understand how stuff executes and analyzer just has no context for that. Otherwise, okay, so I suppose on that note, if it does throw issues with some things, you can suppress script analyzer rules, like there's an attribute as well to suppress specific stuff for your items. Like you mentioned, there's a style that DSL is completely different than the style for command. As you say that, I think I do have those littered in my module where I'm not quite all like the naming, right, the noun verb. I think I had to use an attribute to say, I'm not following that. Okay, you had to be statically excuse the rules. Yeah, I used that too. But in money, your code, you put all these attributes to shut up script analyzer. Maybe nice to be able to put all those into a script analyzer file and isolate all that mess. Yeah, if I had to do it again, I would have used those rules for script analyzers, say this project. This is one that just doesn't apply. So it seems like if you were working with hierarchical data, creating something with hierarchy, you could just easily pass in a hierarchical, you know, custom object or something from JSON based on JSON. But I guess the advantage of going with this DSL approach is that you're able to provide more detail, more, I guess, simplified validation of the user input. And user input is kind of the key there. Like if you're dynamically generating that hierarchical object, like via code, you know, maybe you don't necessarily need any DSL for that at all. Just programmably build that thing. But if you're in front of it kind of writing these things out, that's who the DSL scores is for the end user. So I actually worked with Kevin, my question earlier was a loaded question about whether or not you could run dynamic code inside those script blocks. So the main benefit of this approach rather than creating, for example, nested hash tables for your hierarchical structure is you get a much cleaner dynamic data driven structure generation with this method. I think you might be able to do a lot of the same stuff in like a hash table initializer, but personally I think it looks a lot less clean and I'm not certain. I found that this approach was much cleaner. I can't pick great examples off the top of my head. This really stands out in shines when you're going out to AD and getting machines in the OUs and generating dynamically where everything is in line rather than having to have a bunch of variables and then nesting a bunch of stuff that's now abstracted away in the variables. I think I need to wrap it up here. Please do the speaker evaluations for all of the speakers. This is actually my first time presenting here and I'd appreciate the feedback. Good or bad, I'll take it either way. Enjoy the rest of the conference.
as I start to see how a DSL can solve certain problem in some cases and because you break your design patterns or not. But we'll kind of work pass-through, where I'll actually create a DSL what it does well. And it's kind of fuzzy when keyword does is it takes your commandlet and