Don Jones Toolmaking Part 1 3

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
PowerShell is is one of those things where it's really really easy to get into kind of once you get over a little bit of hurdle and you understand what the commands look like and how the help system works like there's a couple of things you have to know and then once you're into it it's pretty straightforward and for better for worse when the team designed it they kind of designed it to provide an entry ramp for several different audiences and this this was a good idea and that it got people into PowerShell quickly and a bad idea and that it got them into the dark hidden dusting actually musty corners of PowerShell sometimes so you could approach PowerShell a lot like you approached vbscript you could treat it a lot like you would treat a batch file or you could go at it like you would go out of c-sharp program those are sadly all wrong you can do it but you will get yourself down a path where you start fighting the shell because you you get to a point where it says yeah except I'm not really VB script and I really want you to do it this way and it turns out you should have been doing it this way all along and your life would have been better so that's what this session is really all about it's taking that that command that you figured out in the console on the command line and turning that into a reusable tool that fits PowerShell native patterns and practices so we're not going to be fighting the show now for a lot of people this means unlearning some things and that's fine that's what we're here to do for some people it means oh I didn't realize that's why you never do it that way and that's great that's what we're here for so make sure you're asking this question why did you do it that way I'm going to post all my code I'm going to post on on Tom Jones comm it'll be there forever so those of you with computers do not spend your time here trying to copy my code you will not be able to keep up with me I type very fast and I type while I talk I want you to be looking at the code on the screen and I want you to know why it's up there and if you don't know why it's up there ask because that's the important part okay all on the same page there all right so I mean let's just start with something really really simple over here on the shelf what we do with the tool that I'm going to build today is not as important as I do it right it's about the tool-making that's the important bit not necessarily what the tool does so I want to keep the actual task we're performing a little bit simple for two reasons one I don't want to get caught up on that task 2 I want it to take up very little screen space because I have to Jack my font size all the way up so that you can read it so the less the less code I have the more I can fit on the screen so let's just do something simple with a sim get sim instance let's do this from a remote computer DC can you guys read that ok except for the dark gray it's terrible color choice let's do win32 operating system that's a fun one you do have to spell it right that's important it doesn't want to have completed a system let's just that ok that didn't work let's see why nice I'll show you my fun little trick for that inside is my network hooked up I don't know it's not that's why set dawn VM come on you can do it now apparently you can't well here two things first of all host dot private data dot error foreground color equals green there plus it feels nicer doesn't it like that's life-affirming all right let's just do something local done real quick okay what's the computer name here that is why I think I'm in the wrong VM wonder how many of these I have GD do Windows 10 yeah that's the one we're running oh you know why let's do it in Windows 8 this will work get Sim get Sam instance computer a MDC class when 32 operating system there we go okay so this works is that still legible in the back of the room font is sufficiently all right let's get the now I'm going to use the ISE here I don't really care if that's what you prefer don't prefer I'm not going to suggest the ISE is perfect but in a lot of situations it's good enough and I use it because it's what everybody has and my goodness that's tiny there we go everybody at least has this I know if you've seen there's a new enhanced ISE that's out in kind of preview mode you should take a look at that that'd be something great to talk to the team about they'll probably want to hear what you think of it you know Visual Studio code has got support for PowerShell now you can use that sapience got editors out there you can use that if you want to I'm going to use this because it's relatively uncluttered they're a little smaller so I almost always when I begin the tool making process I almost always start in the console and here's a key thing and I'll I'll get into the why behind this a little bit more I never start in the console pane of the ISE the ISE works differently than the console it maintains its memory and scoping differently from the console and there's a reason for that it is a development environment you should not regard it as a test environment you will get different results so I always start in the console that's my lowest common denominator and I always get my command running there first so let's just let's do one more command let's to get a different the class here computer system okay that worked once I know everything works I'll go ahead and copy and paste it into a new script a single thing computer system okay what about that this is a script right now this is officially a tool I could give this to somebody else and they could run my script and it would automatically bat it let's start with the suboptimal bit what what here's not optimal I've got the hard-coded computer name so I mean I could just give this to the help desk and say look if you want to use this just open it up a notepad and change the computer name I mean don't change where it says computer name but changing yeah I can already see this going badly the other thing that this does wrong and this is a hugely important concept that's going to drive a lot of what we talk about this is omitting two different types of objects to a single pipeline when I ran this over here in the console I got two separate pipelines I ran the first command I hit enter that's a pipeline and it produced its output I typed the second command I hit enter it ran it into a second separate new fresh pipeline emitted its output and that's what I got on the screen when I run my script if I were to type my script name and hit enter I get one pipeline and everything that script emits goes to that one pipeline PowerShell does not work as well by default when you've got multiple different things in the pipeline in fact on a routine basis you probably only run one command that deliberately puts two different types of objects into the pipeline tell me what that is derp get child item because it produces file and folder objects right and it produces a pretty good-looking display doesn't it I mean it's it's perfectly legible do you want to know why do you want to know what Microsoft had to do to make those two objects actually work look at this nonsense let's see DPS home and we've got all these XML files in here there we go notepad file system they had to do this all this XML exists solely to make sure your directory listings look good when there are two different objects in the pipeline you are not going to be doing that every single time you write a tool or a script so you need to limit yourself to one kind of output in the pipeline that is important and that often involves significantly rethinking the way you approach your command so when you sit down to begin with you have to be thinking what is the one kind of object I'm going to want out of this that will serve all my needs only one so let's start by getting rid of that hard-coded computer name we'll add a parameter block to the top of our script declare a parameter called string that is a string called computer name and we will use that in place of the hard-coded so we taken care of this the sub-optimal bit now we have a parameterised thing why did I use dollar sign computer name why don't I put host or machine at the end of the day every other PowerShell command that accepts a computer name does so on a - computer name parameter our goal is to be consistent with the rest of the shell when I sit down and look at a command and I want to see if it supports other computers I look for a - computer name parameter be consistent don't invent new crap the whole thing that was a problem with the old command dot XE shell is that every single command had a brand-new set of parameters you had to learn how many of you have ever run I cockles without looking up the syntax in an example first right because there's like 82 different capitals so there's cackles there's de calcul there's Diez ackles there's I have an ex-cop and they all have different parameters well the whole point of PowerShell was to try and fix that with consistency so stay consistent be consistent so now that we've got that in there let's take care of the other bit who thinks we should be accepting more than one computer name sure let's do that - there now it accepts more than one computer name now we have to start thinking really carefully about how we want this to work because we've got a couple of potential things going on this will work perfectly if I run it as is it's wrong but it'll work let's say I run this with three computer names what's going to happen what's the output going to look like it's going to be three operating system objects and then three computer system objects that's probably not optimal that's probably not what we want another problem what if the second computer name I give it didn't there it won't actually terminate PowerShell commands hate to terminate they will go to great lengths to just chug through and keep on going so we're going to get an error message in the middle of all of our output and the thing is because I'm just giving each of these two commands a whole block of computer names I don't actually have any way of knowing which one failed like in in my code I don't get to see which one failed so here's what we're going to do instead we're gonna do a little for each loop and I am immediately going to go indent the code that lives inside of this thing because people who do not format their code correctly are bad people I'm not saying they're as bad as like a pedophile but it's really close how did I get it to do line eight I went to the end of line seven and hit enter yeah yeah I'm I'm I'm quick like a bunny I got to fix one thing real quick because I am enumerate my computer names but I got to fix that right here if you guys distract me I have not had enough coffee to catch that go ahead question I've always tried to avoid doing multi string like that few names and operate from the standpoint of PowerShell - object oriented if I want to pass once when appear next to a tool or function that I'm writing then I should use to get a computer in content or whatever and pipe each computer name as a single entity then I don't have to do before each we absolutely process yeah so the short version of that is why don't I just only accept a single computer name and type computer names to it we're going to get there too but if you look at the patterns that power shell itself follows it often allows both meaning I can do a get child item on a single directory but I can also do it on multiple directories at the same time so I'm trying to stick with the patterns that the shell supports because and this is the reason the shell does that I can't predict how some Yahoo later on is going to need to use this and I don't want to predict I want to create as much flexibility in here as I can so that I don't need to go back later and make some changes this might be the first thing in the pipeline at some point this way will allow you to do tools in - computing parentheses get any computer blah blah yeah there's yeah there's a lot there's a lot more patterns and once we get this to a point where we can run it there's a lot more patterns I can use to input those computer names because sometimes the output of another command is not going to be suitable for this so there's other things I can do to use that anyway so it's just a broader set of patterns we can support okay so we've got this this is now going to go through each computer name one at a time and query both object we're still outputting two objects that's still wrong so to prevent the computer from outputting those we're going to capture the output to a variable so now both of those objects are being captured to a variable they're not going to the pipeline if I ran the script right now what would my output be nothing so Center some major version what else do we wanna hear OS version what that OS version vert version I nobody cares about the OS name Microsoft doesn't even know if the host names our model let's do that CS model M fgr is CS manufacture oh thank God for table completion that's enough right now new object type name PS object property properties so I've created a new object from scratch I have populated it with a combination of information from my two bits of sim data that I retrieved I am actually and this will make a lot more sense later I'm going to save this to a variable as well and then immediately output it I do this for two reasons one there are a lot of situations where before you output that new object you want to do things to it first and so this is a pattern that lets me stick more code in between those and the other and this is a this is a really important code maintainability point I don't like to implicitly write things to the pipeline I like to explicitly do it so that I can come in here and quickly put my eyeballs on the exact spot where my output is being produced by looking for the command write output whereas if I just did a new object it's implicitly outputting that I like to be a lot more explicit notice I do not use the return keyword how many views the return keyword stop it it's only there to fool you Microsoft there are people on the team who significantly regret having done that they call it syntax sugar and they only put it there because developers were going to look for it because they were used to it but it doesn't do what it should do now that's different when you get into classes in version 5 but we're not there we're writing a script not a class don't use the return keyword it doesn't do what you think and all you're doing is creating maintainability problem for somebody else later on down the line yeah so when I instead of using the PS object type accelerator yeah there's about four different ways I could have arrived at that new object I almost just as a personal preference and this isn't right or wrong I almost always take the most explicit route to get anywhere a lot of developers especially c-sharp guys who are used to casting an object with that square bracket syntax will use the PS object type accelerator and that's fine the big difference is that my version will work in older versions of PowerShell than yours so and I again I tend to have a habit toward long form does this basically just say PowerShell cast this to whatever you think it is no this is just a hash table and this is explicitly creating a PS object type of object and then giving it properties that correspond with my hash table so I will have a computer name property an SP version property so there's no casting going on PS object is just a blank object that has a couple of things wired up to it already that is for this exact purpose this is why it exists yeah there is no difference between PS object and the old PS custom object this is just shorter that's all they did it for brevity it aligns well with the PS object type accelerator syntactically so if you're used to one this tends to make sense good questions so obviously I could stack is much into that property hash table as I want to there's another whole technique where you create a blank PS object and then use the add member command to repeatedly add members to it that just gets really lengthy this is a lot more I think concise and it's easier to put your eyeballs all on the data in one spot what's that yeah this is this is moderately faster um you know when you talk about PowerShell and speed you got to remember that it isn't fast period and so the degrees of slowness are what you're you're battling this is a couple degrees less slow than the other way is there a time when you would want to use that member yeah add member is really useful if you're getting an object out of another command already and you want to tack a couple of things on to it meaning you're not creating it from whole cloth that's being given to you and you want to add a couple bits so as an example I could have taken my operating system object and run it through add member a couple of times to just add model and manufacturer and then I would have the whole operating system object plus a couple extra things so yeah you might do that but the add members is really likely keep keep in mind too that I hate people who in a script don't type out all their parameter names completely right bad people nearly as bad as the people who don't format their code which is nearly as bad as pedophiles right so that's the stack right and so with the add member because you have to use all three parameters and the parameter names are very long the thing stretches off the side of the screen it makes for bad presentation so that's kind of like my key thing can I fit it all on the screen at once so at this point we have a pretty gentle little piece of hunk going on let's save it no it's not don't be like that get IOS info one I would normally never put so if you look at that script name real quick I would normally never add the one we're just doing that because we're going to have several iterations of the steering class and I want to save them separately that's all the reason what are you talking about with my spacing casing oh I don't care about that no it's not PowerShell is case insensitive in so am i so a lot of people tell me I'm just generally insensitive but whatever screw those people all right get a boo-boo get - OS tab to complete this is still a script so I have to make sure I'm providing a path right PowerShell won't run a script without a path so we're just going to do this and I got nothing because I didn't give it a computer named Li an 8 1 oops there we go two different computers two different objects come out PowerShell has displayed these in a list form for me that was its default decision do you know why because it only has because it has 5 anything more than 4 is a list 4 or less defaults to a table that's all that is we can get into customizing that later if you want to however two things to note at this point one one thing I see people do wrong a lot this really frustrates me so don't do it up here at the top they'll do you know like an out variable and they'll set it to an empty array and then down here they'll append their new object to that array and then at the very bottom they'll output the entire array the net result of that looks the same and so it's very difficult to understand that you are breaking gravity and people are going to fall off the planet because when I run this in the pipeline I have now created a blocking command normally the pipeline does a pseudo multi-threaded kind of thing where one object at a time is flying through different commands and there's other objects behind it so all the commands can run kind of in parallel there is a time and a place to do this technique for example these sort object-- command does this internally it has to because it can't sort everything until it has everything but unless that's the type of command you're writing don't do this this is something that a lot of folks who are again programmers are accustomed to having to manage their own output stream you don't have to do that here because PowerShell does it for you so just write it to the pipeline write it to the pipeline and let the shell do its thing hand it off walk away okay that's an important piece here ah we do not have any kind of error handling in this this might be a good thing to go ahead and add at this time so where might I expect an error to occur just shout it out line six or seven probably yeah so I kind of have a little bit of a inefficiency going on here that I'd like to kind of correct at the same time in fact before we get to the error handling let's maybe talk about the inefficiency here because we want to build tools that work well can you see anything here that's maybe sub-optimal in terms of how it's using the computer resources I'm connecting twice there is a a a perhaps trivial but greater than zero amount of overhead involved in setting up that sim connection and then tearing it down and then setting it up again and then tearing it down again so rather than doing that I think what I would like to do set up a session oops a new sim instance why doesn't it want to complete my session oh yeah it thinks I'm stuck let's get rid of both of these is it simmer huh all right I've been dealing with a new version too much and they made some changes in one of the betas so let's just hope that this will quick help stop stop stop I'll get sim instance I can't remember if it's our - sim session that's what I'm after so sim session session right sim session session session it's tab completion is a little afraid now that I've made that change where can I expect my error to occur line six is it safe to say that if line six works line seven and eight will also work assuming I've tested them and they don't have a typo or something right there's a difference between a typo I'll get into arguments with people well I just want to wrap the whole thing in a try-catch in case there's a typo no fix the typo right you actually want to see that error message don't just shut off all the errors because some of them are your fault as scary as they are with the read text change it to green right that's why we do that so I'm going to wrap and put a try here where is the sensible place for my try block to end it is not after line seven it is actually all the way down here because if line seven fails I don't want anything else to happen if seven fails I can't do eight I can't do nine and if I don't do eight and nine then there's no damn point doing 10 11 12 13 or under the rest of it so if I get an error on seven I want to abandon ship and go do something different so that's why it structured that way yes your catch of the session then use a throw with an error and terminate the process so why don't I just terminate the product I don't want to terminate the process let me just that iteration before age you'll you'll see why I'll show you we're going to handle the error rather than just barfing it sometimes that's the red crutch sometimes when an error occurs but keep in mind that when you throw within a scope you're not going to throw yourself to the next iteration of a for each you're going to toss yourself out of this scope you're going to the script is going to end and I don't want that happen I want to keep going there is a time and a place for nested right if you think for example that you know line seven here might fail for some reason and line 9 might have a totally different failure that you need to handle then yeah you would have a more specific nested one around that and that would be fine if you've got a way to handle that separately so there's one thing I'm missing on line 7 to make this all work what is it error action thank you error action stop because normally the shell is going to try and keep on going it would run line 7 if there was a failure it would say hey there was a failure I'll get an error message but I will not get a trapa bowl exception and there is a difference between an error message printed on the screen and a tropical exception that I can catch in my code stop forces it to do a tropical exception that I can then catch that is also why we had to enumerate the computer names and only do one computer at a time so that if it breaks I can still loop back up and do the next computer so here's what I'm going to do we're going to we're going to walk into something here this is not the cleanest code yet but we're doing this for illustrative purposes if I fail to connect to a computer I am still going to output the exact same kind of object I'm just going to null out its properties now there's actually a slightly better way that I prefer that I tend to use in production which is this this means I can take the output of my command and I can sort or filter for the computers that were successful or not successful you don't well you got to set them to something because you want them to be there right we want one type of thing going to the pipeline what I don't want is sometimes the object has these properties and sometimes it doesn't has the properties it needs to have the properties and they have to be set to something you could set them to an empty string except on the top half things like SP version art a string there an integer so you can set it to zero but that doesn't actually just zero might be a valid value and you don't have a valid value here you have nothing and that's why I tend to do null it's more the semantics the meaning of it then the actual syntax of what you type I've got some wasted code here there is no reason this is identical code there's no reason for it to exist twice so I'm going to delete it here I'm going to delete it here and I'm going to put it finally finally we'll run after try if there was no error or it will run after catch if there was an error it also runs if you break out we're not worried about that right now don't hit control-c why don't I do a test connection a couple of reasons test connection is just a ping just because I can ping a computer does not mean I can connect to its sim service and vice versa just because a ping fails maybe the firewall is blocking ICMP but it's going to allow sim so because I don't have a working test sim session all it would do is what I'm doing here I'm doing this this also fails faster than a failed ping and even if let's say let's say there's a situation where a computer accepts the ping so that passes and let's say I did have a way of just testing to see if the sim port was open right 59 85 or 59 86 by default that still doesn't tell me that I'm going to be able to authenticate that I'm going to have permission to query so this kind of rolls the whole thing up into one failure ideally good questions notice that I am NOT here's something else I see folks do that really really troubles me I'm not merely querying and then testing to see if I got a null object back right you'll see folks say get sim instance blah blah blah - error action silently continue and then their next line will be if OS is null then I know I didn't get anything don't do that that's terrible there are times when a command is working perfectly and legitimately returns a null value for example get sim instance - computer name whatever - class name win32 tape drive how many of your servers still have a tape drive what would you expect - what does a tape drive that you just asked about thank you thank you now I don't feel old what would you expect to get back from that query normally nothing you're not going to get an error because it is legitimately returning null so it's fine to check for null result as an expected result right if we didn't have a tape drive then I'll skip this section of code that's fine but don't use checking for a null result as a method of error handling see the difference in that case the null result was not the result of an error it was a result of a perfectly normal non error situation why would I return to terminal why would I return the object with no properties keep in mind I might be querying 20 computers and I want some way of knowing which ones failed and I need to output something just take the room apart man that's fine you know they just remodeled just remodeled I want some way of being able to track which ones failed I might want to group those off differently at the end of the pipeline and write them to a text file so that I could try them again later this gives me output for the failed computers so I can do something with it don't know what that is not trying to predict it but it means I'm not losing information for every object in I get an object out I will not still see an error message nope nope if you are trapping an exception if you are catching an exception then there is no exception so there is no error message I would have to throw an error either by using the throw keyword or if you're a PowerShell guy right error because that's actually where we put errors I could choose while we're on the subject you know the problem is that I can't tell my good jokes anymore because everybody knows what the punchline is there are legitimate reasons to use right host and we will cover some of them later this is not a legitimate reason but this is something I see people do a lot right host is very discoverable freaking everybody on the intertubes uses it for stuff here's the number one gotcha with it if I just run this as is in my local computer the fact that I've used right host will achieve my goal right it will it will do the thing I wanted it to do I will get text on the screen telling me that it failed to connect to a computer right short a that text is going to show up right in the middle of my output which is a little vexing just visually be and this is important I'm going to someday want to push this out via remoting to another computer and that computer is going to run this and when it hits that right host that computer is going to try and put that message on that computer screen which I can't see and there is no way for that host output to come back to me so I will this is incompatible with powershell patterns because it does not support remoting everything is supposed to support remoting for the most part all you have to do is not be dumb and PowerShell will do it for you this is dumb so what we need to do instead is change that to a write verbose perhaps a write warning neither of those will interrupt the thing and make it quit working right error will write error to stop it so use 4 boasts used warning whichever you feel is appropriate for you however the verbose pipeline lets go with verbose so we can make a point the verbose pipe line is off by default normally I will not see verbose output how would I turn that on how to out of it what I need to do is enable the square bracket yep that commandment binding tells PowerShell that I want to get all of the common parameters including - verbose I can now run my script let's save this with a different file name so it's my special version this is so I can zip them up and give them to you I don't use a version control version which holders is for people who make mistakes as I do not work in a production environment I do not make mistakes so with get OS info - I can hit - V and I have a verbose parameter built in that will turn on the robos pipeline for me that is there that is one of the five or in version five six pipelines right you've got output error warning for both debug and then in version five you've got information and the remoting system knows how to capture those pipelines and bring them back to you so we're staying within the patterns of what PowerShell does know right posting there is a legit use for right host and we'll get there but it's never in a tool right there germinating and if you're in the the catch block you want it yet so a don't rely on dollar sign underscore to do that because that can get usurped by a lot of things and if you go in and maintain your code and you forget that you use that and you do something that resets it you break your code use dollar sign error square bracket zero to get the most recent error from the error collection but if you want to stick that inside of a write verbose so that you can actually see the error message that's fine and there's a lot of logic reasons to do that dollar dollar underscore is almost always not the greatest thing to use well the dollar dollar sign underscore itself is the object and it can get usurped by a lot of different things so go after it through the error record so we have an e-book on PowerShell dot org you guys know the resources menu has an ebooks thing we have a bunch of free ebooks one of them is the big book of powershell error handling which in fact is not big it was meant to be ironic and it covers the best way to actually get at the errors and it funny there is no one right way because it kind of depends on the exception you're dealing with at the time so it kind of walks you through the process once you do that you also so once i've put command that binding up shouldn't i handle all the other common parameters the beauty of it is I don't have to I get minus verbose for free and if I'm using verbose it'll work I get I get minus minus minus - mais debug for free now I'm not providing any debugging functionality but you'll find that most commandlets don't yeah pretty much everything else is is external functionality like out variable and stuff like that the shell just handles it for you ah so let's talk about that since you mentioned the what F and is that what you were thinking this does not turn on what if or confirm just that does not turn on what if or confirm I have to do this two things number one having done that my commands help will now show - what if in - confirmed I have not implemented that anywhere so we'll talk about that as step one step two my command isn't changing the system state it's a debt command debt commands don't change the system state they don't delete files they don't make any modifications you should not be supporting what if and confirm if you're not making modifications that's what they're for so step that's the second thing I should not have this in here because my command does not need it it does not fit within the pattern not changing anything however getting back to the first thing let's assume that I have some commands in here that are trying to change stuff do I need to do anything special to provide support for - what if or - confirm the answer is maybe because by enabling this on my command when someone runs my command with let's say - what if that's going to pass through to all the other commands I run inside my script so if I've tested them and they're already providing adequate support I don't have to do anything different however there will be times when you want to do something that does not involve running a command maybe you're firing off a method of a dotnet object well they don't have what if and confirm and so yes in that case I would need to manually implement what if and confirmed we'll look at how to do that later we'll write a command that modifies the system State in that fashion but right now we're taking this out of here because it shouldn't be here I invariably will look at scripts that's have supports should process and I will look that script and they have not implemented that support and/or their script isn't actually making any changes and I know that that person found that on Google and they don't know why it's there but it works so they leave it there it only needs to be there if you actually need what if in confirm say again or they use the template I don't use templates real men type also real women any more questions on this where we are he's having trouble swallowing that but it does so yet all the common parameters become implied for every command you run what's that not functions commands commandlets to be more precise so if I were to run this now let's just save it real quick I'll run it with - verbose I have a lot of her votes output that I didn't code because it fell through to my to get sim instance commands which are incidentally not well written do you look at the output operation and nothing complete whoops I could explicitly turn that off yeah I could come in here and put a - verbose : false - shut that off if I wanted to and I've done that in cases where I didn't like what they were producing and it was getting in the way yeah yep what I normally remove that session once I'm done at maybe not usually because once it gets back up to the top of this loop and I I take that object out of context by assigning a new session to the same variable the old object goes away and that session is closed it's part of the destruction of the object so the underlying dotnet framework is told destroy this and the way that object is is coded internally it shuts the session down and then again when I my script finishes and dollar signs session goes out of scope that destructor is called implicitly I've tested that so I know that's the case if this was say a sequel server connection yet you bet I would explicitly close the connection because I've had words with DBAs about not doing that what's up if I were to run play in the ISC no that wouldn't change no it's called right away PS sessions are the same yes yeah they're actually nearly the same thing they both inherit from the same underlying object because they're both using WS man everything under the hood it's yeah it's if you want to approach life with I'm starting a session I'm always going to explicitly close it no one's ever going to make fun of you for that good excellent okay why uh why have I gone through all this trouble to produce an object as my output in other words why haven't I why haven't I just done something like this you know Oh s equals get sim instance computer name whatever class name win32 whatever and then for my output I'm going to say write output write output you know name and then we'll put a control T tab and then status and then you know write output because you got a you have to do that so it looks pretty and that and why am i why would I why is this bad but you will see folks who come from other systems particularly Linux which is a very text file folder based shell in Linux this would not be wrong if you're writing a bash script or a perl script this is the way you do it and that's why everything in Linux comes down to grep sadoc and texts parsing because everything in the shell is text powershell isn't Linux it's not better it's not worse currently we have bash on Windows now for reasons that are not entirely clear to me but whatever it just its power shells different Windows is not a text based operating system one of the reasons command XE has been so problematic for automation is because it's putting a text interface on top of an intrinsically API and object based operating system PowerShell is object based I could not take this output and pipe it to export CSV export CSV wouldn't have the slightest clue what to do with it I could not type it to convert to HTML I could not type it to format anything because I've pre formatted it all so that's why this is the wrong approach and it's why do that's why this is the right approach object based output one kind of object in the pipeline at a time and if your goal is I want to make sure I can capture the names of the computers that don't work then you have to take this type of approach where you're outputting a failure object that looks just like the rest of them and can be differentiated in some fashion from the successful ones good the other thing that we will sometimes see people do this is terrible idea this breaks everything right that right there little known fact that there is white Krypton exploded it's true because format table does not produce objects it produces formatting directives that the shell is is intending to use to build the screen and what's going to happen is it's going to output a set of headers and then a data row and then what's technically a set of footer objects and then it's going to loop back up and get the next computer name and output a set up headers and one data row and a set of footers and the shell is going to get that and go no absolutely not never in a tool never format your code never format your output something else you'll notice and I don't know if you notice you can kind of see you over on the left hand side the the output list right you can see the property names they're not in the right order as of PowerShell three I could have created an ordered hash table here instead of just a regular hash table and then my properties would have come out in the order I set them here and I don't care here's why I don't care one I just apathetic about that sort of thing I just don't care to what the shell is using is a more memory efficient hash table than an ordered one so I'm being less impactful on the system if I get into a situation where I care about the output I will tell it what order I want them in that's what select object is for or format list or format table because as as far as I know to begin with remember I don't know where this outputs going to go if somebody cares about the order well then they can specify it I don't want my tool making any assumptions about how the data is going to be consumed next is there a problem doing select within the object you're returning yeah you're making a decision and an extra computing point where there shouldn't be a decision or a computing point so let's let's talk about the two classes of scripts because this is where we're really starting to draw a line now first of all you guys know where so I kind of started popularizing the word tool making in the PowerShell world you know where I got it how I told you this story would not wood shop really close though my first job out of high school was as an f-14 aircraft engineering mechanic apprentice I went through a four-year apprenticeship and we learned how to take f-14s apart and put them back together again so if you ever buy a used f-14 on ebay call me I actually have the f-14 Tomcat tattooed on my leg and as part of my apprenticeship we had to rotate through all the different shops that we had you know we had an electrical shop and this shop and that shop and we had a machine shop because we were we were set up I was a Navy employee as a civilian employee of the Navy we were set up to deal with battle damaged aircraft and sometimes you had to be able to make your own parts so we had an extensive machine shop so I had to do a two rotation through the machine shop and it convinced me that I never want to be a machinist ever what a filthy disgusting shrapnel filled profession and this is coming from a guy who took more than one or two baths in jet fuel okay and I'm calling that dirty that's where it sets on my ranking and there were two types of people in the machine shop you had the machinists who were out on the shop floor sitting there staring all day she take three hours to cut one swath of metal off and that's what that was the job like this is the least intellectually stimulating thing I have ever done and I have watched paint dry because that was a different shop and it was hot and cold depending on the time of year and you know this was Virginia so it was humid as anything you can imagine and all there's just little bits of metal and oil flying everywhere that stick to you gross did not want to be a machinist the the bits the drill bits that go into the machine that actually cut the swath they're not called drill bits they're called tools and off to the side of the shop in a room with glass windows and doors and sweet sweet air-conditioning sitting in very business casual clothing in front of a computer running CAD cam software were the tool makers the ones who made the tools they designed them and they would be made and then they would go out under the shop floor that nastiness and I decided that if if I had offended God and was going to be put into this trade I wanted to be a tool maker not a tool user so that's why I call this tool making because ideally you get to a world where somebody calls you with a problem and you're like be right back take the type you type typed up in here what do I do with this run it you didn't fix the problem this will fix the problem all times from now on don't ever call me again just run that I'd rather be a tool maker so that's where that came from and so let's talk about the difference between the two types of powershell scripts that you're going to write in your career one type is a tool and the other type is a controller and the purpose of a tool is to do one thing to do it well and to honestly be written with very little context about where it will eventually live it does not know what is creating its input all of its input comes to it through its parameters it has no idea if my computer names are coming from a database or file or if you're being manually typed if they're going to come flying in out of Active Directory it doesn't know and it doesn't care just give me computer names and I'll do my thing and its output lacks as much context as possible here's an object I'm putting it at the pipeline I have no idea what you're going to do with it after that I don't care and I'm not going to make a lot of decisions about it I'm just going to give it to you and the next thing can deal with it a tool should do one thing get here here's a really good example of what I kind of mostly abhor now I've put myself in a situation where I have to check and see if the person provided a file name and if they did provide a file name I'm going to go open that file up and read computer names and if they didn't hopefully they provided computer names this is I'm doing two things here we already have commands that know how to open files so we should just have been using those this command should not be trying to do two things it should be getting operating system information or whatever it's doing and just that it's not going to change anything that would be a separate command because my verb here is get get focuses me in on just one thing always usually approved verbs you know you can run get verb to see a list of approved verbs what happens if you use an unapproved verb you go to hell you do get a warning if you're importing a module that uses unapproved verbs but you really do break the consistency of the shell that's what we're here for is consistency um there's there's no there's no approval for aliases so you can do whatever wacky crazy thing you want to do with your aliases tools do one thing and they do them well so if my job was we need you to automate the creation of new user accounts and that and that includes you know setting up their home directory their Active Directory account their exchange mailbox there's a bunch of different stuff that that involves right but user provisioning that's not one tool that's a bunch of tools and a controller script is what takes those tools and gives them context and purpose the controller script is what might prompt someone for the new employee information or the controller script is what might go to the PeopleSoft database and grab the new employee information and it will then run the tool that creates a new user and run the tool that creates the home directory and run the tool that shares that and run the tool that does whatever other wacky things have to happen and that controller script looks a lot more like a batch file it should be really simple have a really minimal amount of logic and it's not going to be a function it's just going to be a script because it's controlling a business process tools don't know why they're being run controller scripts are the why and if you can create that division of labor think about how much easier it is to debug and test that 82 step controller script when all 82 steps are actually just a single standalone unit that can be tested by itself and further on down the line when the company says yeah we've got this new system now and provisioning has to take care of that - great alright one more tool and I'll slap it into the controller and Bob's your uncle creates better maintainability over the long haul much better modularization and it fits the patterns of PowerShell it works with the shell the way it wants to be worked with good so far jiggle your heads okay all right well we're not quite done we need to do a little bit more fun with this parameter dude up here parameter let's make a mandatory visit of a value from pipeline equals true true value from pipeline pipeline by property name equals that's a lot to type help message whoo ah crap all right well I don't know how to get back no no oh all right that's good that's a lot do I prefer help messages this way or in comment based help yes I like both and I will teach you why I like both you will learn alias is fun because I've hooked this up to do pipeline binding by property name my script will accept an incoming object that has a computer name property no object actually has a computer name property right you go to get ad computer it's got a name property or maybe a CN or you know fqdn whatever whatever you choose to grab but it's not computer name so for that matter I could do this that alias allows me to hook up non-standard inconsistent parameter names to facilitate pipeline parameter binding so if I know that one of the uses for this is going to be accepting an object that has a host name property I can go ahead and create a specific handler for that without actually having to put a bunch of code in place the shell will just figure this out and make it work make sense so the help message let's talk about him if I run my little script get OS info not typing any parameter names what should it do when I hit enter it should prompt me because I've coded that as mandatory look at the third line there but the last line before the prompt that's where help message gets used and that's why I use that in addition to comment based help which we'll get to in a bit and anybody not use comment based help good that was a trick question we would have asked you to leave okay should we test the pipeline stuff make sure it works you're very trusting group I do have a DC it's named DC oh oh where the um there are bugs in different versions of PowerShell about aliases not working for for property names yeah so if I if I had to make a decision here's here's what I would do first of all let me let's see DC equals new PS session computer name DC yep import module just a sec this is a PS PS session session name active directory oh darn it because I call it DC yay okay so his point and this is where we'll repeat the question is if I get all my 80 computers they have a name property now I have not hooked up a name alias here his question was in many occasions even where i if I were to put a name alias there are there are some bug situations where it will not pick that up for pipeline parameter binding and so he said so what I wound up doing instead is I wind up calling this parameter itself name just to make sure it will be able to pipeline bind name coming out of that command that's a choice I choose not to I choose I choose the path of consistency and in that case what I would do is this sorry I got that backwards be right with you I would take this over the convenience yes because I don't actually expect the end-user to be doing this all that often I would probably be giving them a controller script anyway that wrapped that specific task up for them or and I'll show you an example of one a little bit later yeah I tend to err on the side of consistency and maintainability and let me tell you a little story about why I like stories we've time for story short it how many of you still map Network drives yep we've been mapping Network drives since forever right netware mapped Network drives how many to have login scripts that do that how many of you do with GPP why did it take so long for Microsoft to put map drives into group policy there's an answer they had to buy a company to do it they did yeah do you know why there's actually a reason why it took them they have a complete corporate blind spot around maps drives because they came to a point in their their their organizational history where they said these things are stupid they're not sustainable like there's only 26 and by default you use like you know three or four or whatever else and they're not memorable and so internally they stood up a DFS tree and told their users map drives are going away you can go to whack whack Microsoft root whack sales white just here's your UNC's and a bunch of their users said and I'm sure this is exactly how many of you proposed that and how many of you were told no we'll never be able to remember that it'll be too hard and Microsoft said well then you'll have to get a job somewhere else because this is what's happening and you're going to be able to learn it and you just are and you still have to get your job done and you're not allowed to use this as an excuse for not getting your job done we'll just have to let you go and miracle of miracles no one lost their job funniest thing in the world so if I have to choose between consistency and maintainability and following the right patterns versus letting someone be lazy screw your job but I'm also an engineer and I like empathy yep I would just steadfastly refuse nope I'm not going to do it why are you lazy turn it around man make it their fault I'm going to write it down for you on a piece of paper and we'll tape it to your cube screw up I have met very few security people that actually know anything about computers so I can usually run my way around that too as my company tries desperately to build out a large InfoSec library good times oK we've got some right verbose action going on in here look is dooms not in here good I'm not going to do the comment based element and don't tell her don't tell her eyes look the only reason I'm not putting comment based help is because I know you guys know what it is if you don't know what it is look it up it's very straightforward I just want to save some room on the screen here right now that's it that's all and after however if I did do comment based help I would put it at the top right there okay help is not my most important feature it's the second most important feature shipping is the most important feature no I in a production script I absolutely always use comment based help I do it every time I often write my comment based help first because it serves as my functional specification for what I think I want this thing to look like and then I go and code it up and make sure it matches it is very important I don't want to say space here's something that almost nobody does that I wish everybody does which my command here does not in fact require the active directory module but this is an example I do see a lot of folks who will hand code a lot of code to see if the module is available see if the module is loaded fit you don't need to do any of that if you depend on a module this is the right way to document it this is the second right way to document there's another way that we'll come to in a moment that's actually batterer but this is this is this is a good declarative way of saying I need these things as minimums I screw this one up all the time is it just administrator it's run as yeah but yep I didn't realize it put a space in there that's good if something has to be administrator put that in there don't don't fool people about it because you know when you open up a new shell instance it unless you've worked with your shortcuts it's not admin by default which might be good I mean that might be what works for your environment but if this needs to be administrative then now in my case does my code need to be run as an administrator in order for it to work probably it does probably it does by default your remote sim will only accept members of the local administrators group as well as members of the robot management group the underlying WMI repository which is the sim repository is the same is that it are the same thing only administrators are allowed to query that thing remotely that's the default permission and you really don't want to dork with that permission because a there's almost no way to automate it and B you'll break everything I mean everything anybody use system Center you will totally break system center completely so so I probably would leave that in there let's pull it up right now just for fun what else what else yep I didn't comb until version yeah run as administrator' we picked up in version 3 yeah yeah requires also picked up module in version 2 and then it was only version in version 1 which was kind of pointless I know it doesn't exist you seen a way so when you're developing to make sure say you're developing a version 5 you want to make sure you don't usually think that you need to version 5 ah yeah 3 yeah no so here's here's the question and this is a really good question and it's something the team needs to wrap their head around let's say I've got version 5 on my computer here and I know I'm going to plan to deploy the script machines that are running versions 3 4 and 5 is there a way to make sure I'm not doing something that's unique to 5 and just not realizing it and the answer is statically today using the native stuff no now what you're essentially asking for and the right thing would be in Visual Studio you can target a specific version of the framework and it's got libraries that tell it this is in version 2 and this is in 3 and if you try to use something that's off version it'll squiggle it for you we don't have that native tooling nah yep engine under certain you can run the engine under a different version but that won't there's there's no programmatic way for it to tell you that you've done things that are off version some things yes certain engine based language things yes but it doesn't know for example what the remoting stack looks like or whether certain commands are out there so it's not complete but so in your idea of ISE is just for development test everything you need test everything in the console in the console crazy you then run the console under a specific version and when you do your testing you would fly yeah older version uh well it wouldn't flag it it would error right but that but now you know hey I can't do that now that's yes and no there's there's no static declarative way which is what he's after there's try running it in whatever I mean I could also just run it on a computer that had the version which is actually safer because that whole engine switch is is not like a complete running of that engine bro it's I've seen instances where things would work there but then if you took them to an actual machine they would fail so it's not tight sorry fight it out pester and neither pester nor script analyzer will simulate another version you could certainly set up analyzer static rules but you would have to know to look for everything right like you could set up a rule that looks for the - in operator which didn't exist in version 2 we only had - contains but you'd have to set that up and it would just be a static analysis and it's still not going to ever be like a hundred million percent especially as the language itself gets bigger and more complicated but he's peace first yep our so - version only supports the - Oh engine correct you can't you can't force it to drop two three or four now and that's because of the way those engines install - was the only one they did a side by side with yeah festive put it in our containers in here in to do that that's it for yes well yeah so I mean the idea is if you're doing a build pipeline with with pester you would spin up a VM that has version two and one that has version three and one that has written four and see if your code explodes and all you're doing is automating the same thing we were talking about run it and see if it explodes tester certainly makes that a little easier you know if you've got a good build pipeline like teamcity or something that can spin up stuff in fact the reason that the PowerShell dot or build service which is a free build server we offer to open source community projects that you didn't know about that yeah we don't have really good marketing department it's on the resources menu so you know browse around down again the reason we use teamcity instead of the azure based pipeline that the PowerShell Gallery offers is because the PowerShell gallery only uses four and pester works all the way back to two and so we wanted to be able to target version 2 version 3 version 4 so that's why we're using teamcity to do that and yeah that's the only way to do it is to just try it and see if it explodes all good questions what else what else do you want to do - that we didn't actually test it I knew there was something else DC when a one get OS info well that half worked so let's talk about how the command lives when you run a command declaratively just by itself and you're giving it parameters it runs from top to bottom so if I give it ten computer names when my command runs when it hits line one all 10 computer names are in that computer name parameter variable right and it just runs from top to bottom and that's why I have a for each in here so I can enumerate through those ten and do them one at a time but remember what I told you about the pipeline the pipeline is this kind of pseudo multi-threaded thing where only one object speeds through the pipeline at time so when I run this in the pipeline like I did here PowerShell looks for a begin block a process block and an end block needed to do to do to tap the begin block runs first and that happens before pipeline parameter binding occurs so your pipeline bound variables will be empty at that point any explicitly typed variables like that the person actually typed out on the keyboard those will be populated in the begin block and then the process block runs and your pipeline variable gets one object which means my for each is redundant in that mode and that's fine it doesn't hurt it to be there it's going to enumerate the one object the one time the for each is there to support a different run scenario my process block will run completely from top to bottom for each input object so if you pipe in ten things that's going to run ten times and then the end block will run I see a lot of folks get really upset and confused because they've targeted pipeline parameter binding in their parameter block with those two value from pipeline and value from pipeline by parameter by property but they haven't put in a process block and when that happens you wind up only getting the last object out so let's save that and try again there we go the for each the for each loop is redundant because it's only numerating one thing but it's perfectly happy to only enumerate one thing internally like if you go disassemble a lot of the the native PowerShell commands like if you D compile them into c-sharp they look exactly like that they do the same because that's how you have to do it I will see folks go through this big logical alright I need to see if this was bound and if I'm in PI parameter mode or if I'm in pipeline mode and I'll take now you don't need to do any of that we have it you have this redundant for each and no one cares it doesn't cost me anything test it out and you and you have to have it to support the other scenario you can't because when I run this not in the pipeline when there's no pipeline input it ignores begin process and end it just runs straight top to bottom so whatever is in the begin block will run because I have it in that position but it just sort of implicitly ignores the fact that the begin the process in the end keywords are there it just yes yep OOP crap we wasn't there that would not work that's the other part that needs to for each and this that needs the for each and that is why we support both scenarios we don't just lock it down to pipeline input because there's times when getting the pipeline input to work is going to be more pain than it's worth and that is an easier workaround that is not as performant right let's how many how many computers do have an active directory more than two right so in this scenario I'm forcing the shell to go query all the computers and then remember all of their names in its memory and then dump all of those into that computer name variable in one go where I'm going to enumerate through them again I'm wasting processing power and memory but sometimes that's the only scenario that works so we support it yet what hit enter oh I'm afraid yeah so to do that kind of exact same thing get ad computer filter star select expand name right what's in the pipeline at this point are just string objects that will perform better because now it's going to run in the pipeline it's going to run my begin block which is empty and the process block will execute once and it'll be able to stream those things in as they come in from Active Directory and actually what's fun you can't do it with a script command like in version 4 of PowerShell there is a way for a native command but there's a way for a native command --let to send a stop signal back up the pipeline for example Active Directory is going to start sending computer names over I'm going to start processing them select really only once the first four so select after it gets four can send a we're done up the pipeline and every command that is capable of responding to that will stop processing so things can wrap up a little quicker why why would I not put that you should ideally um it turns out get eighty computer does not respond to the stop signal so it doesn't matter where I put it my function will functions will respond to that because the shell itself knows how and in this case is script because we're not even up to a function yet is there any way from inside a script to know what's in the pipeline none because you only have one you have no idea what else is coming you would have to accumulate them all and then you wouldn't know until the end and you'd be blocking the pipeline why you did that yeah well progress bar is kind of are what they are that's why you don't see a lot of progress bars in PowerShell because it's not often known in advance what 100 percent looks like so yeah I'll take a break I think we have snacky that's outside so let's take the team in a break we'll come back at 10:45
Info
Channel: PowerShell.org
Views: 73,165
Rating: 4.8913045 out of 5
Keywords: powershell, windows powershell, techsession, powershell summit
Id: KprrLkjPq_c
Channel Id: undefined
Length: 86min 10sec (5170 seconds)
Published: Wed Apr 13 2016
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.