Powershell retarded



  • So last couple of days I was struggling with a powershell script to install some service into a VM on the first boot so I can automate upgrading them and scaling the set. What looks like an hour or two job took me a couple of days, because:

    • The place where the documentation says logs are has logs from the system tool starting the script, but the log from the script itself is elsewhere.
    • Ok, the documentation suggests some powershell command to get it (from the developer machine), but the correspodning az subcommand (which is otherwise usually more complete and easier to use than the powershell module) does not seem to show it.
    • Powershell has entirely too many ways to set up handling of errors and warnings, so my attempt at changing the destination (before I ran across the standard location) was not successful.
    • The documentation says:

      If you're using Invoke-WebRequest in your script, you must specify the parameter -UseBasicParsing. If you don't specify the parameter, you get the following error when checking the detailed status:

      The response content cannot be parsed because the Internet Explorer engine
      is not available, or Internet Explorer's first-launch configuration
      is not complete. Specify the UseBasicParsing parameter and try again.
      
      Warum, kurwa, just warum?
    • That option seems to change the way the response is interpreted. When I tested it by hand without it, the Content of the response was interpreted as ascii string, but with it, the Content was space separated character codes, because the server didn't have content-type configured and was returning application/octent-stream. Which makes sense, but why can't the command just choose one way to behave always?
    • Powershell scripts, though apparently not the command-line, default to not wait for external commands you start, so if I just say & $InstallPath --install, the service won't be there yet when the following line tries to start it. Kinda defeats the point of being a ‘shell’.
    • Working around that with
      Start-Process -Wait -NoNewWindow $InstallPath "--install"
      
      works when I run the script manually, but when running from the utility, it looked like it wasn't being called at all. And I have no clue why, no error was reported, it just did nothing. So I ended up using the New-Service command instead, that at least worked.
    • Oh, and that command can't set up recovery options. Services allow setting restart behaviour for when they crash, but guides I could find say to just call the old sc.exe utility.

    :sideways_owl: :wtf_owl: :sideways_owl: :sideways_owl: :sideways_owl: :wtf_owl: :wtf_owl:


    Filed under: advice on how to deal with this shit welcome, but I reserve the right to keep ranting



  • @Bulb Yes.

    You have only met the tip of the iceberg of Powershell fuckery. However, it's better than the other built-in scripting languages available on Windows.

    Also, just use sc.exe... it's a simple command for a simple thing.


  • I survived the hour long Uno hand

    @Bulb
    Also more fun that you'll get to deal with as you mess with this further... which version of PowerShell are you using, and are you getting the correct blog post / documentation for it.

    At this point there are two supported versions of PowerShell, but there are breaking changes in syntax or options between them, often resulting in the same command having two different ways to work with it.

    PowerShell 5.1 is the version that's installed with Windows 10/11 and auto-updated by system updates. It's Windows specific and uses Windows-native (legacy) APIs all the way down. It's :airquotes: supported :airquotes: but in maintenance mode at this point because they went with the new hotness with PowerShell Core.

    Which has been rebranded and the official supported version of PowerShell Core is now PowerShell 7. PowerShell 7 is cross-platform, and was supposed to be a direct upgrade from the original PS Core (now known as PS 6). But just like with .NET Core land, after their first attempt at cross-platform, they figured out they fucked things up, so there's a ton of breaking changes even just from PS 6 to PS 7, on top of the original breaking changes from trying to decouple from native Windows land. Obviously some cmdlets within PS 7 (that work with inherently Windows things) are still going to be Windows specific, but mostly things you work with in PS 7 are written with .NET Core5 / cross-platform principles in mind.

    Invoke-WebRequest is one of those modules that underwent major changes between PS 5 and PS Core7. For starters, the -UseBasicParsing flag was removed and turned into a placebo in PS 6, so if you're using new PowerShell and consulting documentation that's talking about -UseBasicParsing, you're going to have a bad time. And as much as I piss and moan about how Invoke-WebRequest's specialty child Invoke-RestMethod handles errors in PS 7, in general I'd recommend using PS 7 for any new code that is dealing with either cmdlet.

    Also, similarly, if you're working with Azure VMs, there're several currently maintained modules for interfacing with Azure or the Microsoft Graph API in PS 7, but they've cut off support for new functionality in those modules in PS 5 land (which basically means that it's PS 7 or bust for those modules now... as long as you don't also need Exchange Online modules in the same PowerShell window session in the wrong magic order, but that's a whole other pile of 💩).



  • @Bulb Also, if you want to launch a program and wait, use the call operator and capture the output to a variable (use $_ if you don't care what's in it)



  • @Bulb said in Powershell retarded:

    & $InstallPath --install

    Isn't that a background invocation?



  • @izzion said in Powershell retarded:

    a whole other pile of 💩

    A whole other pile of pile of poo ?



  • @Jaime said in Powershell retarded:

    @Bulb Also, if you want to launch a program and wait, use the call operator and capture the output to a variable (use $_ if you don't care what's in it)

    😕 The only thing the link says about capturing output is

    If you need to capture a return value from the new process, store the output the process generates ( stdout and stderr ) and control the style or visibility of the Window, then use Start-Process which makes all those options available.

    @BernieTheBernie said in Powershell retarded:

    @Bulb said in Powershell retarded:

    & $InstallPath --install

    Isn't that a background invocation?

    No, but actually yes, which might be the reason it didn't work. The documentation of prefix & explicitly mentions that you put another & to the end to execute it as a background job, while the prefix & just requests treating the following string as a command name.

    But in practice external commands (whether directly or with prefix &) are executed in background when … something. Which is what I'm running afoul of here.



  • @Bulb This is an actual line from one of my scripts:

    $sitesxml = & "$psexecpath" /accepteula /nobanner -h ('\\' + $servername) c:\windows\System32\inetsrv\appcmd list site /xml
    

    It waits and returns the output to the variable.



  • @Steve_The_Cynic said in Powershell retarded:

    @izzion said in Powershell retarded:

    a whole other pile of 💩

    A whole other pile of pile of poo ?

                        💩
                  💩 💩 💩
         💩 💩 💩 💩 💩
    💩 💩 💩 💩 💩 💩 💩



  • @Jaime Thanks. If only the documentation actually said it somewhere so normal person would run across it at latest on second read.



  • @Bulb Don't get too excited, I think I wrote this code while looking at someone else's script, possibly on Stack Overflow. However, this behavior definitely is undocumented, and probably buggy. I recall having to deal with an issue where the output was truncating. I probably should have fixed it by changing to Start-Process with -RedirectStandardOutput, but that switch only supports a file name and I hate writing intermediate data to the file system.



  • @Jaime … and might differ between the different versions mentioned above. Also I don't actually care about output. I just want plain old leave stdout and stderr wherever and raise an error (subject to the error action preference) if the process exits with non-zero status as one would expect from something that calls itself a shell.



  • This post is deleted!


  • The title of this thread sounds like an insult that a programmer would use.

    "You're not just retarded, you're Powershell retarded."



  • @Gern_Blaanston To me, it sounds like something a programmer might say whose linguistic abilities were less than his programming abilities. "Powershell (is) retarded!"



  • @BernieTheBernie said in Powershell retarded:

    @Steve_The_Cynic said in Powershell retarded:

    @izzion said in Powershell retarded:

    a whole other pile of 💩

    A whole other pile of pile of poo ?

                        💩
                  💩 💩 💩
         💩 💩 💩 💩 💩
    💩 💩 💩 💩 💩 💩 💩

    It's piles of poo all the way down.



  • :phb:: Embedding C# defeats the purpose. Why didn't you just write this in PowerShell?
    Zenith: :chloe:
    :phb:: But PowerShell would've been easier for a non-technical resource to maintain!
    Zenith: :laugh-harder:

    Oh, and to add to everybody else's gotchas, wait until you have two different versions installed side by side.


  • BINNED

    Status: Happy that I never bothered to learn PowerShell.
    And I thought bash sucks ...



  • My use of PowerShell is minimal. I like that it saves history, and that it recognizes the command ls, and that it has pretty colors, but I've never put the effort in to become adept at it.

    Is there anyone here who uses PowerShell because they think it's substantially stronger than the Windows command line?



  • @jinpa Yes. I often do things like "Show me all of the members of the Domain Administrators group that haven't logged in for the past 90 days". It's a PowerShell one-liner.



  • @jinpa I've used PowerShell a couple times because it has less/different limitations than Batch or VBScript, but mostly I use the old Command Prompt because I've used it for so long. I even changed Windows Terminal to default to cmd.


  • I survived the hour long Uno hand

    @jinpa said in Powershell retarded:

    My use of PowerShell is minimal. I like that it saves history, and that it recognizes the command ls, and that it has pretty colors, but I've never put the effort in to become adept at it.

    Is there anyone here who uses PowerShell because they think it's substantially stronger than the Windows command line?

    I maintain several custom-built PS modules for work. Mostly as syntax sugar wrappers around the processes we do (user management, API access to third party systems for user or device management, etc), so I'm just doing PowerShell all the way down and not messing with compiled CLR code. Trying to do those sorts of runbooks as batch scripts would be pure chaos.



  • @jinpa Same here. My experience has been that it looks enough like a PHP/C# hybrid to fool you into thinking it's a programming language only to hit you with a decidedly batch scripting behavior that's inexplicable if you're thinking like a programmer instead of a scripter.



  • @Zenith To paraphrase, with powershell you can have any kind of behaviour you want, as long as you don't want it to be consistent.

    The low level bits (variables, objects, methods) behave close enough to perl/PHP/VB. It's the external commands that don't behave like in other shells, and the ‘commandlets’ that behave as a weird hybrid between functions an what external commands would be like in other shells that make it so weird.


  • I survived the hour long Uno hand

    @Zenith
    PowerShell is more functional style than OO style programming, but there's not a lot of scope control for your variables out of the box.

    I've had pretty good success with custom developed modules once I

    1. Got enough experience to learn the little trip wires in PowerShell
    2. Went away from the "master script with hundreds of lines" style of programming like is more common in bash scripting, in favor of "small, as idempotent as possible" functions instead.
    3. Put those functions in separate files (this is the most important part, in terms of actually getting some degree of scoping and separation of concerns)
    4. Defined those functions as full up PowerShell functions, with parameters and explicitly passing data to the functions in the native PowerShell way
    5. Make heavy use of Write-Verbose or Write-Debug, and never ever ever rely on the automatically write-out-to-console behavior of just plopping your output variable onto its own line of code. This is basically required, if you're going to do the encapsulated function approach, because the calling function is probably going to capture the output of the child function and do something further with it, and that means the calling function will capture everything and anything written to the Output channel, via Write-Output or plopping a variable or output of some other PS function just on its own line of code. (As a convention, most of my functions are set up to only output one line of something, and I preface that line with return to make it obvious that it's intended return, even though the return keyword isn't necessary except for early returning, and using it for early return isn't guaranteed to prevent other code later in the function from generating output in certain cases).

    I'm still not great with error handling (and PowerShell doesn't really go out of its way to make that easy), and you really do have to be super careful with what you dump to the Output channel. But if you do take a PowerShell native approach, it can work fairly similarly to what a "normal" "Uncle Bob" style program would, and give you good control over being able to rerun just the failed steps / starting from the failed step in a large sequence of setup scripts, without having to take comment scissors to the code every time something goes wrong.

    I wouldn't write a ticket scalping sales site in it, but for gluing together frequently repeated administrative tasks into a single runbook that can prompt your tier 1 helldesk guys through the information they need so that they don't fuck up user or machine management, there's lots of power in it. And I would recommend anyone who was venturing into learning PowerShell to start with PowerShell 7 at this point - it's better in most ways and the little foibles it has aren't any more foibly than PS5, just new and exciting different foibles.



  • @Zenith said in Powershell retarded:

    :phb:: Embedding C# defeats the purpose. Why didn't you just write this in PowerShell?
    Zenith: :chloe:
    :phb:: But PowerShell would've been easier for a non-technical resource to maintain!
    Zenith: :laugh-harder:

    Oh, and to add to everybody else's gotchas, wait until you have two different versions installed side by side.

    I installed the latest version of Powershell which is 7.3 or something. It's the newest version, so it must be better, right?

    Then I discover that a Powershell script I have doesn't work any more, because a couple of functions that were in Powershell 5 were removed from Powershell 7. :wtf_owl: :facepalm:

    Fortunately, I was able to pull a copy of Powershell 5 from a backup, and it works. So how I have 2 versions of Powershell.

    Microsoft: Because Fuck You, That's Why.



  • @izzion said in Powershell retarded:

    early return isn't guaranteed to prevent other code later in the function from generating output

    :wtf_owl:



  • @Zenith said in Powershell retarded:

    only to hit you with a decidedly batch scripting behavior that's inexplicable if you're thinking like a programmer instead of a scripter.

    Like continuing on error by default. What kind of an insane person figured that'd be a good idea? I rarely use PowerShell but the one thing I do remember, because I have to, is $ErrorActionPreference = 'Stop'.



  • @Deadfast I always got caught by return value scoping, where is expect a void return to do nothing but PS decided to print it or pass it to another function. Batch files have some scary parameter/return behavior for its fake functions but you know it’s a kludge so you’re on the lookout for it.


  • Discourse touched me in a no-no place

    @Parody said in Powershell retarded:

    I even changed Windows Terminal to default to cmd.

    I've always launched the Windows Terminal by doing +R cmd. I didn't realise you could do it the other way round. 😊