Using the PowerShell API from .NET

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
okay so my name is rob hull um i'm an engineer on the powershell team and um i want to talk to you today about uh using what i'm calling the powershell api for the purposes of this talk so we're going to talk about well first of all i want to define what i really mean by the powershell api and i want to motivate it a little bit i want to uh tell you why it's worth knowing about and when you probably want to use it um then um i'm going to show you okay how do we use it to just sort of run ordinary powershell basic implications take a look at manipulating outputs and inputs from commands invocations that we run take a look at composing uh these invocations into more complex things like pipelines and statements as well actually uh and then we'll look at using the api with um you know more sophisticated tooling like configuring your own run spaces running it across different threads using it asynchronously so what what why i'm using this term powershell api what do i actually mean by this normally when people say powershell api um they're referring to everything that the powershell sdk exposes and that i think is probably the right terminology to use but within powershell there is a type called powershell and it provides a sort of api to run to construct and run powershell commands and i'm referring to that when i say in this talk powershell api i don't really know what else to call it to be perfectly honest with you i guess i could call it the powershell type or the powershell object but um really i'm interested in its properties as an api and so i'm calling that's what i mean by the powershell api it looks like this you create a powershell object you add a script to it and then you um invoke it something along those lines right it's very simple to use at least at first but as you start building more functionality around it you realize that there's a lot that it can do it had a lot of bells and whistles and knobs to twiddle and you get quite a lot of configurability with it and it can do a lot for you without really having to work all that hard to be honest um it's usually the the api that you're going to want to use um in most cases or at least in a lot of cases when you are running powershell from.net so like when is that why why do you want to know about this api well usually when you're running powershell from.net you're running commandlets and there are other ways to run um powershell script from commandlets such as the invoke command api and even just scriptlock.invoke and they can be quite useful and in the context of commandlets sometimes you should prefer them but when you want to do something more structured or more intricate or more complicated then running using the powershell api is going to give you a lot more configurability and it's possible to use the powershell api pretty much anywhere where you would use one of the other apis um another example i see a lot is when people are writing dot net code that isn't implementing a command lit but is designed to be part of a like a wider powershell module or is going to be invoked in some way from powershell indirectly and they want to run powershell sort of deep within their program well um one straightforward way to do that the the easiest way to do that generally is to use the powershell api it's not always necessarily the right thing to do but um it's it's often you know and it's an important sort of tool to having to build any standalone executable that you write in.net that you want to um bring powershell along with it you know that you compile with the powershell sdk that you want to run powershell the really the um you know the primary way really the only way that you should be running powershell from a program like that is by uh is through the powershell api and as an extension to that if you ever find yourself implementing a powershell host where you define a class that implement that extends the ps host type um so examples of this uh the powershell executable um the integrated console nvs code um the ise are all powershell hosts and hosting sort of like as a proper noun or powershell refers to as a host you're almost certainly going to want to use the powershell api to accomplish that um i also think it's just useful to know about the powershell api because if you're a powershell developer you know how to call.net from powershell very easily and it's worth knowing about how to call powershell from.net um it's worth understanding you know all of the concepts that work there and and also give you some insights into things like how powershell does it itself so at this point we're going to just jump right into the code i think because really this is a you know a talk about how to use an api and the best way to demonstrate that is by showing you directly how to do it so first of all when you use the powershell api to create a powershell object and you know it's pretty straightforward you take this powershell type you can't use it doesn't have a public constructor because it is more involved to set up but this powershell.create is quite well known and relatively well documented um and you get an object back and it's of type powershell and something that's important to do here is to make sure you dispose it in this case using a using block because often this will create resources required to run powershell if you don't dispose it then those resources go and register themselves somewhere and you'll start leaking things and if your application is supposed to run for a long time it doesn't shut down immediately after invoking powershell then that's probably a bad thing so you need to make sure that you are cleaning up after yourself properly so the basic example of running powershell using the powershell api looks like this we say you know something like get child item um path dodge rather artificial example but um it's useful for our purposes and then we um invoke it and collect the results so we say powershell dot invoke and then because we're just trying to demonstrate um that this sort of works without doing anything particularly useful here we're just going to print out the results we get them so um when we run this program yes i have saved it good we get the contents of the current directory and so this is quite interesting in on already in a number of ways one get child item is using a relative path and that implies some kind of state of powershell which is its current location and you'll notice that its current location is this ps api folder and that's exactly where i am and so you know whether or not you um like intend to use it powershell uh has a concept of its own state and it's important to be mindful of that ideally you shouldn't be running any commands that depend on that state too heavily or if they do you should be managing that state very carefully um but it's worth bearing in mind that um that this this sort of does play into things and you know without you really thinking too hard about it too the other thing to note is that like if we just run get child item ourselves we get very different looking output to this and that's because when powershell does this it runs the formatter on the output whereas for us uh we're simply getting a wrapped result we're getting a file system info object back from get chart item wrapped in a ps object and when we call rightline on result it simply calls tostring on that which calls to string on the underlying object and we can sort of prove this by showing that if you do the same thing with powershell you know explicitly you get exactly the same output okay so we have sort of like a you know you could get away with writing programs like this you know for a while i'm sure but there are a bunch of things that we can improve here the first thing is sort of a stylistic one but it's that the powershell api is a fluent api so you can chain its indications like this and um often people chain invocations down like that i find that quite readable um to sort of build the state before your invocation that means that you know all of you you don't have to redeclare powershell all the time with you know semicolon semicolon semicolon you just put it all into one sort of bundle and then ship it off it doesn't always you know as we get more complicated we might we'll see that um doesn't always bear out but you know this works you know exactly the same way as we expect um another sort of what initially seems to be a style note is that this ad script phenomenon is kind of a pain um because we've just given it a raw string to execute which um you know in general executing strings as programs inside your program isn't really a good idea it can be unsafe if you're taking user input it can be uh it's hard to analyze um is it you know you can hide all kinds of evil inside a string right instead of that but instead you can do something like this where we add the command and the parameter as separate pieces into a more structured api so instead of using that script add command takes the name it says command well that's misleading it takes the command to invoke uh much like in powershell if you did say so so where um get child item is is really a string pass to powershell's invocation syntax that means that you can't just put arbitrary script in here it must be the name of a command this also supports native commands and it supports paths to scripts on the file system but you can see that when we run it again we very excitingly get exactly the same result we got last few times but this is good confirms that you know we are able to restate our program in a more structured way but in fact that's not the main virtue of using this add command add parameter methodology the real advantage is when you have something that when you have a commandlet that takes a real uh runtime type so convert to json is a great example where if we had to write it in a script then whatever we were converting to json well we already had to convert it to a string just to convert it to json and that's really unhelpful but what if instead when we're writing this from a net program there's a reason why we have powershell hosted in our process we're trying you know powershell is an object oriented shell we want to be able to pass objects to it so what happens if we have a hash table where [Music] we really do you know have an object and we want to pass it to powershell well we can do something quite straightforward there which is that if you look at the api for add parameter you can see that its value is an arbitrary object type so in fact um we can pass through our fully like our full.net runtime object straight through to powershell and we can see that yes we really do get the output that we expect um another interesting point here is that um you might have noticed that there are actually two overloads for add parameter so we can add a second parameter for example with get child item and see this parameter doesn't take a value well what's a parameter that doesn't take a value in powershell it's switch but if we say add command get child item add parameter path dot and parameter recurse it's going to be the same as simply providing this switch parameter recurse and again if we run the program we can see that we get lots and lots of values this time because we recursively enumerated our current um current location so that sort of covers you know how to manipulate parameters what about the output that we're getting well let's look at um a slightly more uh slightly sort of more output oriented command like get data in this case um well let's look at the help for get date we see that get date as a commandlet takes this date parameter but that date parameter should be of type date time and previously when we used convert from uh convert to json and things we were able to provide a full a fully instantiated object to convert and indeed with date time we can say okay well let's create a date time object oops and add 10 hours so so that when we run our program it gives us the like date for uh 10 am today okay well that's nice but if you were in powershell you wouldn't write date time today at hours 10 you would write a string that much more succinctly expressed that day for example um providing just this sort of 10 am string will have the same effect and when we run our program we can see that this actually does work and the reason for that is because powershell provides the usual parameter binding logic that it always would just like in powershell itself when you run getdate date i mean the powershot formatting applies but it gives you is able to convert a string to a date time value and is able to do the same thing in this circumstance too because it runs the type conversion logic on inputs that you supply now in dot net that's probably not always desirable you probably should be constructing strongly typed inputs to a command that you're using but it's worth knowing that this does happen because it means that you know your inputs might be converted by powershell if they don't match the parameter type another nice thing about get date is that you we know especially with this invocation what type it puts out it creates a date time object so we can actually specify that with the powershell api by saying that we by using this generic overload of powershell's invoke method to say okay i expect date time a date time back and when we do this we have to be ready to receive a collection of data objects and then all you have to do is print those when we run the program again we'll see well exactly the same output but what's interesting is that we can now do things like um manipulate that output directly right because it's it's now of the ordinary.net type that we would have gotten back if we you know called a different a.net api so we can manipulate it just the same as ever this is really handy but you might get the type wrong what happens if we get the type wrong well let's find out turns out we surprisingly enough get a big error and it says okay well can't convert this value date time to this value int because there's an invalid cast um but um you can see that we're using the language primitives api so that suggests that in fact powershell just like it converts parameter values can also convert outputs so let's and indeed even though get date produces a date time value because powershell knows how to convert date time to a datetime value to a string value when we specify that we want string as output and because it's possible within powershell we get a string back um you can really see the difference in this particular scenario because we're running as a subprocess so you always get a string back i can't prove it to you in the command line um but uh so this is really nice in the case of a command like getdate where we know what type it's going to return and for a particular invocation you might always know that but some commanders return different types based on the the way in which you invoke them and some of them don't return an ordinary.net type at all so for example let's look at convert from json convert from json takes a string and it gives us back a ps custom object so for example let's just make a really uh simple json object which has this key x and it's value one when we run convert from json we're going to get back a ps custom object how do we like deal with that in powershell well the answer is kind of staring us in the face but but let's let's watch what happens so when we run this we get back some object which we currently represent as ps object and it gives us this you know actually reasonable representation of a ps object um the truth is that ps object is like perfectly capable of wrapping and describing these types um and um if we want to do something like get at the value of the property x all we have to do is use the peer subject api to do so underneath this is a ps custom object and you can use that instead if you prefer but you can unwrap it using the base object property on the ps object but it'll behave approximately like this does one thing you might find convenient though is instead passing this to a.net dynamic uh type so that you can directly reference this just like you would in powershell so if we did something like uh if we had this as a powershell variable we'd be able to say you know object.x well using dynamic in.net we can do exactly the same thing this is because powershell's ps object implements special goo under the hood which allows dynamic to sort of hook in to its property accesses and things um this isn't really what i'd recommend for something like convert from json because the input isn't like well defined it can change and so your properties will change a lot you know depending on what you have you might want to programmatically change the property access and that's not really what dynamic is for what it's much more useful with is something like invoke script analyzer where the module itself defines um strongly typed.net objects so if we you look at the output of script analyzer you can see that the output is a real.net type and it comes from the script analyzer assembly the problem is that from our program we can't reference that statically so we can't there's no value we can put in here that will compile to say yes i'm looking for a diagnostic record like script analyzer outputs but instead if we look at something like x dot message we can see the message of x squared analyzer defines a formatter so we have this sort of nicely formatted output but if we wanted to manipulate a record like this without too much fuss and we know that it's from invoke script analyzer so we know what properties are defined on it then we can simply use this dynamic cast to reference those methods directly so it's those properties directly so you get sort of quite a nice result like that without having to do too much work uh in your own code um okay so we have a fairly good understanding of parameters or at least so we think um i want to talk a little bit more about when you use scripts directly with the powershell api so for example let's come up with um a really silly script that just takes an argument and adds three to its say is that going to be so like useful to us here well the answer is yes and so the first thing to note is that when you use add script all the normal default uh or automatic variables are populated and um for arguments you can add them simply by using the dot add argument so we can say okay add five and let's uh convert this back to an ordinary ps object and run our program again and we get eight which is five plus three so it works as we expect we can actually also define it as a parameter so we can say dollar p p plus three we say added parameter p and we're going to set p this time to say four you can see that this script you know works just like an ordinary powershell script where we have a param block at the start and we have a body and when we run it this time we'll see that we could set the parameter p p plus three p is four so plus three seven and indeed we get 7 exactly as we describe is this particularly useful or interesting um not really in and of itself but it starts to get more interesting when you do something like you know look at what else is defined say the input variable so input is the automatic variable for pipeline input right so when you pipe to a script that in a script block say the input variable contains everything that was piped to that script so far and in order to specify the input with the powershell api we want you can see there are actually multiple overloads of invoke and input is common to many of them so let's provide that input here from truth so now we're going to take every um input to this script and then add two to it okay so now we've got 345 which is exactly what we expected and this is quite interesting on a couple of fronts one is that um you know our input was populated properly and we added everything so this is just sort of worked magically but another interesting thing is that we realized now why we have always get a collection of ps object powershell is about enumeration and pipelines and any script that you invoke could return zero or more results and so you know you're not like the powershell never guarantees that you get exactly one or exactly in results you're always by default the powershell invocation is always going to return some collection so this is um you know interesting but it's a little bit cumbersome and it still suffers from the same issue that we had at the start which is that defining things in script referencing powershell variables in a string isn't really ideal instead we'd really like to be able to define a pipeline using the powershell api and um you know so that ideally we could just take what we have here and then you know implement it in a more structured way well that's also actually very easy to do um so we can just take our 4-h object command and we can use add argument so add argument works for you know commands of any kind and when used with the command it simply sets a positional argument for each object's positional argument here is a script block and we want it to add three to everything that it sees so whereas we previously used dollar inputs for our script what's actually happening here is that we're defining that dollar input is um implicit at the start of the pipeline we create with the powershell api and sent in through this input parameter to invoke and then sent through for each object that is to say when you provide an input the rest of your structured command becomes a sort of pipeline definition so we're now running something like this pipeline let's have a look so you can see you know we're adding three to each thing and we started one two three and we get four five six so that's exactly what we expect but what if we wanted to add more things to our pipeline what if we wanted to do something like where object okay so we're getting four five six what if we want greater than four well all we have to do is call add command again this time let's for the sake of a good example add it as a named parameter instead but on where object this script lock is named builderscript and we have to call scriptblock again and instead we'll say greater than 4. so now we're expecting to only get five and six back and indeed that's exactly what we get now what you might notice about this is that we're defining this sort of pipeline using this fluent api and that's quite nice it's also reminiscent of net zone link um one thing that link is good at is carrying the types through and with powershell we can do the same kind of thing we also have to make sure that it's an enumerable um so you select and we can also use where where okay so we're multiplying by two we got five and six let's demonstrate that we're actually doing things again i i more than ten so this time we're only expecting 12 back indeed that's exactly what we get and one thing where link is really convenient here is if you only want one result back you can simply say use link to say first now when we run the api run our invocation we don't have to use it for each loop to do anything we just use links nice integration with this sort of collection output to get exactly the result we expected this is really nice when you only expect one result um and you know it's interesting that these link and powershell both have this sort of pipeline you know fluent api concept at work that's no coincidence they are both implementations of um a sort of enumeration uh pattern and um i personally find that uh you know obviously i've used 4h object in where object here they map perfectly to select and where you often don't have much reason to use specifically these two commandlets in powershell from net but um it's often really useful if you call you know a big uh commandlet information and then use link to sift through it later so you sort of get out of powershell as soon as you can um the other thing that you can do with the powershell api let's just uh convert some of this back here um the other thing you can do with the powershell api remove this input is um actually compose statements with it so for example we can say something like x equals two that's not going to give us any output but the powershell api has one more method the add statement method um which allows us to effectively put a semicolon in the middle of our powershell program and then we can add another script which references x and effectively is like setting x and then on the next line referencing it so when we run this we'll see that we set said extra two and then yes we actually do get two back as a result um one thing that's interesting to note here is that ad script has a parameter called use local scope and by default that's false and that means that by default add script dot sources the script that you give it and defines it globally well defines it into the um sort of higher level context if we use logo set use local scope to true and we run our script again we'll find that we get no output or we get a null output because um when we use local scope x is only defined within this script so once that's done you know after this statement we don't get any output back again so i want to talk uh a bit about more about this powershell create part of what we're doing here let's simplify the actual powershell that we're calling back to something really simple again um so we're basically back to the start where we have our original sort of rather uninteresting output um but what's interesting is that so far every time we've run powershell we've been using the standard default powershell create method turns out there are some overloads of this and they all work slightly differently but one interesting one is runs based mode so by default when we create powershell with the empty overload of this what actually happens is um when it comes to invoke time powershell tries to work this powershell object works out if it has a run space to run on or not and if not it'll create its own um and it thinks of itself as owning that run space and so at invoke time before and invoke time before it invokes uh before before it runs it will create this run space object open it run the command output the results and then when we dispose of the powershell object it will also close and dispose of that run space this is one of the reasons why it's really important to make sure you always dispose of the powershell object but one of the easiest overloads here is with this runs space mode enum it has two values there's new run space um which turns out is essentially the default uh version of this it's just more explicit um and you won't see any change in the output and runs just the same in fact as we sort of work through this we're not going to see much change in the output here but it might be important for more sophisticated commands um this tells powershell okay always create and open a new run space um but the other one is much more interesting it's run space mode current run space and when we try to run things this time we're going to see something kind of weird which is that we get an exception and it says we can't use the current run space because there is no current run space you know you idiot what are you doing so usually this is really useful when you're running from something like the say the end processing method in a commandlet if you're running in net code on a thread that already is running with a run space in it then using this means that you can reuse that run space and you can use any of the definitions available in it you can manipulate it change it you can hook back into that run space from your.net code and run arbitrary powershell in it if we want to use it here then we have to explicitly create that thread sorry that that run space that that local run space so run space is in powershell threads are often associated with run spaces which is to say that not every run space belongs to a thread but in general if powershell isn't given a run space like explicitly it'll look on the current thread um and it'll look for this property it'll this it looks like a static run space.default run space but it's actually a thread local static which means that on a different thread it could have a different value so we can explicitly create our own by assigning to it and then we have to make sure that we open this run space and now when we say use current run space things start working again so this is kind of like um a a lot of work to go through just to you know get the same result and in this case yeah there's no really good reason to use this this is usually for reusing something that you already run space it's already defined for the current thread usually because you're running underneath something like a command commandlet um so more likely what you want to do is create your own run space so you can uh do that pretty much the same as we just did it's just that rather than assigning it to a static which you know would then feasibly be reused by anything else running on this thread later um it's kind of like our own special run space and you can provide that as input to the create method here it's one of the overloads but generally i think the right way to do it is actually to assign it here because you can see that run space and run space pool kind of have a symmetry over that and so we say okay well let's just assign our run space and now uh we can run things as usual the big difference is that say we wanted to reuse this run space later um it wouldn't be destroyed by um disposing of this powershell okay so before we do something like talk about using run space pools i think it's worth looking at running powershell off of the current thread so not running it synchronously um and in particular um we can use the invoke async api this is available from powershell 7. there's a way to use the begin invoke and end invoke methods in if you're running against windows powershell 51 or against powershell standard and net provides convenience methods to convert it back to a task a task returning method instead but since this exists in powershell seven i'm just going to use it directly um now by default the return type of this is actually a task of a ps data collection so let's have a look at that this isn't really a huge departure for us so far um we're just going to take this task and yeah that's the result um and we should see that when we run this we get exactly the same output as we uh are used to getting and all that's really happened is that we've kicked off powershell running on a different thread and we can then get the results here so a better illustration of like a proof of that is that um we can say sort of sleep too um and then we can say console right and now when we run our script we immediately see we're running powershell but then it takes two more seconds before we get output in fact if we sort of say we define the script to have some kind of output then we'll see that we say we're running powershell we wait two seconds and then we see the output reported okay well um there's kind of more here this ps data collection type is a bit conspicuous and it's you know interesting to think okay what why why is this important here what what's the meaning of this type in this object well it turns out that we can find our own collection here and we can provide that collection to be used by powershell in its asynchronous invocation um when you do this you kind of want to ignore the output at this point this is now going to just be an ordinary task uh and your main interest is um in the contents of this output collection so we've now replaced the actual result of the powershell invocation with uh the contents of our data collection uh that we created ourselves um and what we're going to do is we're going to keep powershell off then we're going to wait for it to finish and then once it's finished we're going to look at the collection to see what's in it and we get exactly the same result that we had before what's really interesting about this is that this type actually defines events so for example you can say okay well um as things that added to this collection i want to know um in real time or like you know concurrently what's being added so we can say something like output index it's kind of a strange face but that's how it works and when we run it again we can see okay we're running powershell we sleep for two and then as these things are added we see them added to the collection and then um as we um and then they're printed at the end because they're in the collection still now this seems like it's slightly strange thing to do but imagine when you run powershell in the console if you have something like one two three sleep two and then you press ctrl c you expect that in fact let's let's try it you expect that based anyway myself uh one two three sleep two you expect to be able to cancel that with control c but you're you still get partial output well the equivalent of cancellation with the powershell api is something like um okay let's do red dot sleep one second and then we'll say powershell.stop now this stop method is going to cancel whatever is running at this point which is probably going to be what is going to be this script and so let's see what happens when we when we run this again so we've added these things to the collection but then we throw a pipeline stopped exception so imagine we didn't have this ps data collection we got rid of all this we're back to the original invoke async and we stopped powershell even though we would have had some partial output we would have thrown a pipeline stopped exception and we wouldn't have been able to collect or use any of that output but when we manage powershell ourselves when we manage the output collection ourselves we're perfectly able to catch this pipeline stopped exception and when we run our application this time we get to keep our partial output okay so this is like a fairly cute example right um what about something more interesting what about a more interesting application of the powershell api let's look at writing a service um that runs powershell asynchronously and i'm going to show you a few tricks here so first of all i kind of set up some boilerplate here you know there's some stuff behind the scenes where we run i power we have this icon service thing and it's going to get picked up by something i wrote earlier but we have to obey this create int max run spaces the reason i've done that is because i want to show you a run space pool so let's for example use let's create a run space pool with the run space factory 4 3 8 run space pool and then you can see that in the overloads they're the mid run spaces and we've defined maximum spaces and then we're going to want to create a powershell service around this runs based pool so let's set up our power cell service object with its constructor here a little bit so now we have sort of this powershell service with a run space pool but we need to work out how we're going to actually execute powershell asynchronously well the first thing is this returns a task and we're going to want to be as asynchronous as possible so this method shouldn't like blocking shouldn't block for anything and so that means that when we were going to use a using statement like this before we can't actually do that anymore because we'd need to block in order for the disposal to wait so we're going to have to solve that problem a little bit better in the future but for now let's just say um that um okay we're going to return powershell dot add script and we're going to add a script to it oh we're going to say bar powershell equals powershell.38 we're going to add our run space tool to it and we're going to return powershell.add script and then we're going to call invoke async so the first problem we have is that invoke async returns the wrong type and it's not really easy using this invoke async method to return the right type so instead we're going to use continue with which is a task concept but which lets us do some things after the task after the actual powershell invocation has finished so the first thing we can do well okay i've jumped ahead a little bit one thing that you might notice here is i'm saying i'm going to return a string and that's kind of weird like don't we want um bull powershell objects well i'm to come clean with you i'm designing this to be like a web service and i just wanted to return a powershell like the string of the powershell output that i would expect and there's a really a simple way to do this i can pipe the output of my script to outstring and what outstring does is it calls if you've never worked with it in the formatter in the console is it actually run the powershell formatter on the objects that it receives and then gives them to you as a string rather than printing them so for example gci to outstring is going to give me the normal looking output but um it's actually one big string um so i can say you know i i it it's just a giant string that doesn't actually give you the it's there's no objects underneath it it's been rendered into a string rather than into the console but in this case that's kind of what we want because we want to return a string um over our like service request so when we continue right let's continue with the powershell processing task is now done so the riddle of how do we dispose of powershell is now kind of easy to set up we can say okay powershell dot dispose is the first thing that we should do because our object is now complete sorry our object is now done being used and we don't need to and so this is the best possible time we can dispose of it the next thing we should do is we need to transform what we got back which is going to be a set of strings in powershell a collection of strings into one string to to transform back so let's let's use a little spring builder we need to build up um and we're going to look through the result that we get in this task get yet result we're going to append each uh of the results that we get and here because of the way the async api works we're kind of forced to call this base object you know do it do it the hard way rather than getting a nice generic result but we're able to uh cobble it together and now we have a nice string so this looks sort of vaguely like it's going to work i actually have this set up to some degree so let's see what happens okay running a server need a new terminal and we're going to say invoke rest method um vp local post uh what was it 5000 um script equals get oops get well get child item oh not found oh that's right because i didn't actually specify that we're running under um ps oh haha yes we forgot to open our run space pool this is why i create methods important is because it does something that isn't just a factory operation it actually um needs to open a run space which is something that you know you wouldn't want to do in a constructor you need to be able to do because this is going to alter state in some way by opening a run space you want a factory method to do it okay let's restart our service service line increase the size of that a bit now we run this and look we get a nice string output but we have a problem what what if somebody's malicious and tries to send us a really long running script you know this is going to sit there for 10 seconds so it would be really nice if we could do something like define a timeout one way to do that is with a cancellation token source um and um so we create a cancellation token source and um we should make sure that we dispose of that as well after we're done but um what we want to do is okay first of all let's set up a default timeout so like cancel after two seconds two seconds is pretty long time for a web request we don't really want it to go on further than that um and then we're going to want to say okay for this cancellation token source uh for its associated cancellation token we want to register um something to happen when it's cancelled um we're going to put that down here what do we want to happen when we cancel this token source well we're going to call powershell.stop and that's going to cancel our powershell execution um and um just make sure that things have plumbed through properly um we're going to say okay if we get a pipeline stopped exception which is what happens uh in the example where um this um where this task is uh where we call powershell.stop on this task we're going to throw a new operation cancelled exception get another l i needed the one l there we are um and we'll say script with wrapped okay so now we've linked our cancellation token we've actually you know asp.net provides or our caller provides a way for us to send a cancellation token in we also define a default timeout because it's going to be easier for me to demonstrate the crucial part is that we register the cancellation callback for that cancellation token to tell powershell to stop and then when powershell if powershell is stopped and throws a pipeline stopped exception we have to make sure we translate into that into an operation cancelled exception so let's uh restart our server and server and back in our original terminal we can see that if we run gci we get the good old same stream we always got but if we say sleep 10 well time's ticking away oh it says that we timed out because now we have this ability to manage the pipeline stopped exception and what's kind of interesting about this is that okay well that's great and we also ran a we're also using a um a run space pool which means that we can kind of run all of these concurrently and they'll all fail but the nice part is that because we have as many as 10 run spaces operating at the same time sorry i didn't tell you that but um let's say max run spaces is set to 10 we can um rather than having to wait two seconds you know times five is ten seconds we can see that in this case we basically immediately got you know within two seconds or so we got results for all of them and that's because using a run space pool we get a sort of concurrency up to max run spaces anyway so hopefully this is sort of educational about how to actually use um the powershell api to do something relatively sophisticated if you're curious about what's actually happening here i'm actually using a an asp.net web server you might have recognized that in the server output here um and um when we run uh invoke rest method um it goes and executes the uh powershell script on the web server which is a terrible idea don't ever do this you know the like there's there are professionally hosted versions of this like the power the azure functions powershell worker this is just something awful that um when you you know invoke it um it runs powershell on the server and then gives you the formatted output back but hopefully that's kind of educational about you know how far you can really go with the powershell api and you'll notice that we didn't spend that many lines on this you know this is sort of like 40 lines of code more or less and half of it is dealing with things like this cancellation stuff so that's that brings us to the end of the talk um hopefully especially with that last example you can appreciate that the powershell api is um you know quite capable disabled was capable of doing you know quite sophisticated things and in fact if you're trying to run powershell you know especially in a way like we were in that last example as a service there's not really another way to do it you know there are a couple of small ways but powershell api is by far the easiest um and you know it makes it relatively straightforward to um you know it doesn't get wildly complex as your scenario gets more complex even in our last example you know it still wasn't like the number of places where we're touching powershell doesn't really get too too difficult um if you uh you know if this talks sort of like raise a lot of questions for you or if i went over anything too quickly or if you wanted more detail on any of it then you can find this talk and the associated code and actually a much larger document that i wrote around this material at this link but um eventually it'll be you know somewhere that powershell owns and it's more official this is just where it lives while i've been writing this talk um so yeah hopefully you enjoyed that um hopefully this has been informative and hopefully um you uh feel a lot more confident and able to use the powershell api in your own code that you write after watching this
Info
Channel: PowerShell.org
Views: 7,685
Rating: undefined out of 5
Keywords: powershell, windows powershell, techsession, powershell summit
Id: GI5Y0ldHyac
Channel Id: undefined
Length: 62min 54sec (3774 seconds)
Published: Thu Dec 30 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.