Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
nielsm
Jun 1, 2009



milk milk lemonade posted:

I'm writing a password reset script and I figured out how to get it to say "Sorry! That user doesn't exist" but I want it to return to the start of the script when that happens. Is there an easy way to do this? I've looked at a few things like erroraction but I don't think this applies when I do GetADUser and want it to either display the results or say "whoops!". If I need to start over and look at it from another angle I'd appreciate any tips on where to start!

Look into how classic text-mode menu systems have been written.

The typical approach is to have an "input loop" for each "menu" or other input-step, that will take input from user, validate it, and only exit the loop when a valid input has been received.

Roughly, untested and maybe incorrect:
code:
$inputValid = $false
while (-not $inputValid) {
  $input = Read-Host "Username to reset"
  $foundUser = Get-ADUser $input -ErrorAction SilentlyContinue
  if ($?) {
    $inputValid = $true
    Write-Host "Found user: $($foundUser.Name)"
  }
  else {
    Write-Host "No user found."
    $otherUsers = Get-ADUser -Filter { Name -like "*$($input)*" }
    if ($otherUsers) {
      Write-Host "Did you perhaps mean one of:"
      $otherUsers | Format-Table Name,samAccountName,DistinguishedName
    }
  }
}

Adbot
ADBOT LOVES YOU

milk milk lemonade
Jul 29, 2016

nielsm posted:

Look into how classic text-mode menu systems have been written.

The typical approach is to have an "input loop" for each "menu" or other input-step, that will take input from user, validate it, and only exit the loop when a valid input has been received.

Roughly, untested and maybe incorrect:
code:
$inputValid = $false
while (-not $inputValid) {
  $input = Read-Host "Username to reset"
  $foundUser = Get-ADUser $input -ErrorAction SilentlyContinue
  if ($?) {
    $inputValid = $true
    Write-Host "Found user: $($foundUser.Name)"
  }
  else {
    Write-Host "No user found."
    $otherUsers = Get-ADUser -Filter { Name -like "*$($input)*" }
    if ($otherUsers) {
      Write-Host "Did you perhaps mean one of:"
      $otherUsers | Format-Table Name,samAccountName,DistinguishedName
    }
  }
}

Awesome! Thank you. I don't do any sort of script writing or anything and I want to learn Powershell really bad. I'm trying to put do things via technet and frankensteining blog stuff together, but what you've posted here gives me a good idea of how to get where I want to go.

milk milk lemonade
Jul 29, 2016
Uh so Powershell is like the best thing ever for server administration right? I just bulk-added 388 accounts from a csv file and it ignored everyone who already existed! (don't ask)

This must be how cavemen felt when they discovered fire.

Dr. Arbitrary
Mar 15, 2006

Bleak Gremlin

milk milk lemonade posted:

Uh so Powershell is like the best thing ever for server administration right? I just bulk-added 388 accounts from a csv file and it ignored everyone who already existed! (don't ask)

This must be how cavemen felt when they discovered fire.

Yep, and now you're like in the top 10% of system administrators just for taking this basic step out of the ocean onto land.

Walked
Apr 14, 2003

So I've been cranking away as a PowerShell module I'm pretty happy with.

https://www.powershellgallery.com/packages/cDscDocker/1.1.0
https://github.com/walked/cDscDocker

This little guy will configure docker on a Server 2016 host, and join it to a docker swarm if configured as such.
If anyone else is dabbling with docker, I'd be more than welcoming of any feedback.

Requires Powershell V5 / Server 2016

I've been able to lab test this with a bunch of Server 2016 hosts; and all is well so far.

Hughmoris
Apr 21, 2007
Let's go to the abyss!
Vague question but does anyone here use Powershell for things outside of sysadmin type work? For web scraping, text parsing, console applications etc...? I like exploring new languages but I'm not going to need it for any sort of administrator duties.

Methanar
Sep 26, 2013

by the sex ghost
I used powershell for text parsing and manipulation, although in the context of systems admin work, mostly because it was the only way I knew how.

Pile Of Garbage
May 28, 2007



Hughmoris posted:

Vague question but does anyone here use Powershell for things outside of sysadmin type work? For web scraping, text parsing, console applications etc...? I like exploring new languages but I'm not going to need it for any sort of administrator duties.

I once wrote a script to scrape and harvest documents from exposed Lotus NSF indexes on the internet but that was just something casual.

thebigcow
Jan 3, 2001

Bully!
I wrote a script to pull invoice information from Fastenal's website, scrape it for item details, and add duplicates together so it matches our purchase orders. I have another script that turns a mess of garbage from our payroll software into a file that our 401k provider can take, complete with Safe Harbor matching calculations. I'm working on another script to take a mess of sales emails from different sources and turn them into a csv file that our crm software can handle so no one has to type ever again.

Pick a task that seems like a computer should be doing all of it, break it down into parts, join my quest for fire.

Toshimo
Aug 23, 2012

He's outta line...

But he's right!

Hughmoris posted:

Vague question but does anyone here use Powershell for things outside of sysadmin type work? For web scraping, text parsing, console applications etc...? I like exploring new languages but I'm not going to need it for any sort of administrator duties.

I did this the other night and it's gross but it worked so...

code:
$base_url = "http://killsixbilliondemons.com/"
$output_dir = "C:\tmp\k6bd\"
$files_to_download = New-Object System.Collections.ArrayList
$files_to_download.Add('http://killsixbilliondemons.com/chapter/wielder-of-names/page/12/') > $null

while($files_to_download.Count -ne 0){
    
    $current_file = $files_to_download[0]
    $current_file_base_array = $current_file.Trim('/').Split("/")
    $current_file_base = $current_file_base_array[$current_file_base_array.Count -1]
    
    if (!(Test-Path "$output_dir\$current_file_base")){
         Invoke-WebRequest -Uri "$current_file" -OutFile "$output_dir\$current_file_base"
    }

    $temp_array = @()

    Get-Content "$output_dir\$current_file_base" | % { $_ -match "http://(?:killsixbilliondemons`.com|[a-z0-9]+`.cloudfront.net)?/[^`"'?`)`#`<]+" > $null; $matches.GetEnumerator() | % { $temp_array += $_.Value } }

    $temp_array | Sort -Unique | % {
        $temp_name = $_
        $current_file_base_array = @($($temp_name -replace $base_url -replace 'http://[a-z[0-9]+`.cloudfront.net').Split("?", 1))[0].Trim('/').Split("/")
        $current_file_base = $current_file_base_array[$current_file_base_array.Count -1]
        if (!(Test-Path "$output_dir\$current_file_base")){
            if (!($files_to_download.Contains($temp_name))){
                $files_to_download.Add($temp_name) > $null
                echo "Test: $temp_name"
            }
        }
    }
            
    $files_to_download.Remove($current_file)
}

sloshmonger
Mar 21, 2013
i used powershell to read through a CSV of what location I visited each day to generate formatted expense reports. Makes it a 2 minute thing at the end of each week instead of 10 minutes. I should be breaking even timewise sometime in 2018.

Mo_Steel
Mar 7, 2008

Let's Clock Into The Sunset Together

Fun Shoe
I actually use PS for a bunch of file manipulation: searching through every txt file in a directory for a search term, deleting every other line in a text file, replacing the Nth character in each line of a csv file with a different value, checking for the existence of a specific file every night and reporting on it, etc. It's really useful because you can also embed the help documentation for the script you've made right into the file just like with regular PS commands so other users can run Get-Help .\my-script.ps1 -full and get back detailed info on the purpose of the script, required and optional params, usage examples, etc.

If I have to do a task more than once, chances are I'm looking for a way to use Powershell to automate it.

thebigcow
Jan 3, 2001

Bully!
Microsoft changed the formatting for the online help files and now I'm a confused old man :corsair:

Mr Crucial
Oct 28, 2005
What's new pussycat?
Is there a way for a function to evaluate a passed parameter and automatically convert it to the right data type? I have a function that needs to take in a Boolean, which works fine if it's run as a one off from the command line like so:

Add-MyItem -name "Item 1" -enabled $true

Where the "enabled" parameter is defined as a Boolean within the Add-MyItem function.

But what I really want to be able to do is pipe the import from a CSV like so:

Import-CSV AllMyItems.csv | Add-MyItem

With a CSV that looks like

Name,Enabled
Item 1, $true
Item 2, $false

Problem is my script rejects the "enabled" parameter because it gets passed as type System.String regardless of whether I specific it as TRUE, true, $true, or 1 in the CSV.

Walked
Apr 14, 2003

Mr Crucial posted:

Is there a way for a function to evaluate a passed parameter and automatically convert it to the right data type? I have a function that needs to take in a Boolean, which works fine if it's run as a one off from the command line like so:

Add-MyItem -name "Item 1" -enabled $true

Where the "enabled" parameter is defined as a Boolean within the Add-MyItem function.

But what I really want to be able to do is pipe the import from a CSV like so:

Import-CSV AllMyItems.csv | Add-MyItem

With a CSV that looks like

Name,Enabled
Item 1, $true
Item 2, $false

Problem is my script rejects the "enabled" parameter because it gets passed as type System.String regardless of whether I specific it as TRUE, true, $true, or 1 in the CSV.

code:
[Boolean]$newVariable = [System.Convert]::ToBoolean($variable)
Hopefully thats what you're looking for; grabbed out of a script I'm using that pulls boolean values out of XML


edit: If the CSV has the $ in the value, you'd need to strip those it looks like.

code:
[Boolean]$newVariable = [System.Convert]::ToBoolean(($variable).Trim('$')

Walked fucked around with this message at 16:06 on Nov 17, 2016

anthonypants
May 6, 2007

by Nyc_Tattoo
Dinosaur Gum
If casting to boolean doesn't work, make an if statement that sets a variable depending on what's in that column.

e:
code:
foreach ($row in Import-Csv -Path c:\path\to.csv) {
  if ($row.Enabled -eq "1") {
    Set-ADUser $row.Name -Enabled:$true -Confirm:$false
  } else {
  if ($row.Enabled -eq "0") {
    Remove-ADUser $row.Name -Confirm:$false
  } else {
    #etc.
  }
}

anthonypants fucked around with this message at 17:34 on Nov 17, 2016

Mr Crucial
Oct 28, 2005
What's new pussycat?

Walked posted:

Hopefully thats what you're looking for; grabbed out of a script I'm using that pulls boolean values out of XML

I was hoping not to do a conversion within the script, because if a user is running the CmdLet for 1 item and specifies a Boolean in the correct format then the conversion is unnecessary. It just seems weird to me that PowerShell doesn't have a way to natively understand Booleans within CSVs.

I think the best solution for me is to change the "enabled" parameter to be a string with a ValidateSet ("true","false") filter on it, and just make sure users know to type "true" rather than "$true" from now on. This works fine later on in the function because ultimately the "enabled" parameter is passed as text into a JSON object anyway so it stops being a true Boolean at that point.

anthonypants
May 6, 2007

by Nyc_Tattoo
Dinosaur Gum

Mr Crucial posted:

I was hoping not to do a conversion within the script, because if a user is running the CmdLet for 1 item and specifies a Boolean in the correct format then the conversion is unnecessary. It just seems weird to me that PowerShell doesn't have a way to natively understand Booleans within CSVs.
It's because those values aren't booleans, they're explicitly strings.

uvar
Jul 25, 2011

Avoid breathing
radioactive dust.
College Slice
PS is getting a promotion -

quote:

Today we are excited to be releasing Windows 10 Insider Preview Build 14971 for PC to Windows Insiders in the Fast ring...

PowerShell in the Shell: In an effort to bring the best command line experiences to the forefront for all power users, PowerShell is now the defacto command shell from File Explorer. It replaces Command Prompt (aka, “cmd.exe”) in the WIN + X menu, in File Explorer’s File menu, and in the context menu that appears when you shift-right-click the whitespace in File Explorer. Typing “cmd” (or “powershell”) in File Explorer’s address bar will remain a quick way to launch the command shell at that location. For those who prefer to use Command Prompt, you can opt out of the WIN + X change by opening Settings > Personalization > Taskbar, and turning “Replace Command Prompt with Windows PowerShell in the menu when I right-click the Start button or press Windows key+X” to “Off”.

Fun fact! It’s PowerShell’s 10 year anniversary this week...

https://blogs.windows.com/windowsexperience/2016/11/17/announcing-windows-10-insider-preview-build-14971-for-pc/

MC Fruit Stripe
Nov 26, 2002

around and around we go
I didn't even know Win+X was a thing - I'm more excited about learning that than this change!

The Fool
Oct 16, 2003


MC Fruit Stripe posted:

I didn't even know Win+X was a thing - I'm more excited about learning that than this change!

It can also be accessed by right clicking on the start button.

Frohike999
Oct 23, 2003
Ok, I'm hoping one of you can help with this one. I'm trying to write a script that will recursively go through a folder and rename all the files in it. That part I'm ok with. The problem I'm having is how to actually do this rename. The files all have the format of "abc1234.txt'. I want to keep the abc prefix and the .txt, but I want to add 100000 to the number in the middle. It's not necessarily always a 4 digit number in the middle, it could be abc1.txt or abc100001.txt. I'm going to keep working on this at work this morning, but if anyone has any insight I'd really appreciate it.

nielsm
Jun 1, 2009



Frohike999 posted:

Ok, I'm hoping one of you can help with this one. I'm trying to write a script that will recursively go through a folder and rename all the files in it. That part I'm ok with. The problem I'm having is how to actually do this rename. The files all have the format of "abc1234.txt'. I want to keep the abc prefix and the .txt, but I want to add 100000 to the number in the middle. It's not necessarily always a 4 digit number in the middle, it could be abc1.txt or abc100001.txt. I'm going to keep working on this at work this morning, but if anyone has any insight I'd really appreciate it.

A regular expression may work to extract things.

code:
  if ($filename -match "(abc)(\d+)(\.txt)") {
    $newfilename = $Matches[1] + ([int]$Matches[2] + 1000000) + $Matches[3]
  }
$Matches is a magical variable that gets set when you use a regular expression operator such as -match, to contain the matched subexpressions.

pre:
PS> "abc123.txt" -match "(abc)(\d+)(\.txt)"
True

PS> $Matches

Name                           Value                                                                                                                          
----                           -----                                                                                                                          
3                              .txt                                                                                                                           
2                              123                                                                                                                            
1                              abc                                                                                                                            
0                              abc123.txt                                                                                                                     

PS> $Matches[1] + ([int]$Matches[2] + 1000000) + $Matches[3]
abc1000123.txt

Frohike999
Oct 23, 2003
That's perfect, thank you! I was trying a regular expression yesterday but my formatting was wrong and it never made the match. I had moved from it to trying to replace the "abc" with "abc1000" or whatever based on the length of the filename. I'm...not very proud of that.

Anyway thanks again for the help here!

Dr. Arbitrary
Mar 15, 2006

Bleak Gremlin
I'm phoneposting, but there's a way to specify a number format that includes leading zeroes or trailing numbers after a radix.

code:

$a = 348 
"{0:N2}" -f $a 
"{0:D8}" -f $a

Offering in case what you're REALLY trying to do is enforce a consistent numbering system.

Frohike999
Oct 23, 2003
Ok that's great. Nah, what I was really doing was merging a couple of our clients data without overwriting files of the same name. Nielsm got me where I needed to be. The client's happy and I'm happy to not have to deal with it anymore. That's really interesting though, thanks for the idea! I'm still working on learning what I can do, so this'll be great to add.

Toshimo
Aug 23, 2012

He's outta line...

But he's right!
I've got a PS script that just makes a windows form and does everything from there. Is there a way to not hold a blank command window open in the background if running this from a shortcut?

anthonypants
May 6, 2007

by Nyc_Tattoo
Dinosaur Gum

Toshimo posted:

I've got a PS script that just makes a windows form and does everything from there. Is there a way to not hold a blank command window open in the background if running this from a shortcut?
I've asked this question before, too, and if that's what you really want, you should rewrite your thing in C#.

Toshimo
Aug 23, 2012

He's outta line...

But he's right!

anthonypants posted:

I've asked this question before, too, and if that's what you really want, you should rewrite your thing in C#.

Gross

anthonypants
May 6, 2007

by Nyc_Tattoo
Dinosaur Gum
Yeah.

mystes
May 31, 2006

Once you're doing stuff like windows forms, powershell just turns into a worse version of c# anyway. It's just too bad that the syntax is different enough that it's sort of a pain to rewrite powershell into c#.

The Fool
Oct 16, 2003


I've kinda felt that you shouldn't be doing anything with powershell that would require a GUI anyway. Your scripts should be written to be able to be ran headless, and if you need a GUI launcher type thing, do that in a different language.

nielsm
Jun 1, 2009



The Right thing to do would be wrapping the tasks you want to do in a Powershell module, then write a C# app calling that module through the Windows.Automation namespace.

Toshimo
Aug 23, 2012

He's outta line...

But he's right!
Powershell is cool and good because I can assume that it's on every windows box at work and that I can send anyone a script and it'll work the same (pretty much) everywhere and they'll be able to edit it as needed without having Visual Studio or something on their box (also, we're trying to get the people at work limping into 1 language, I'm not going to try and teach them like 4, I want to retire some day).

Powershell is dumb and bad because its GUI support gives me Visual Aids.

If there's another language that's going to be (a) consistently available on all my windows boxes without having to install a whole 'nother software package and (b) can give me GUIs so that I can give things to non-programmers and not have them stare at me blankly, pls let me know.

anthonypants
May 6, 2007

by Nyc_Tattoo
Dinosaur Gum

Toshimo posted:

Powershell is cool and good because I can assume that it's on every windows box at work and that I can send anyone a script and it'll work the same (pretty much) everywhere and they'll be able to edit it as needed without having Visual Studio or something on their box (also, we're trying to get the people at work limping into 1 language, I'm not going to try and teach them like 4, I want to retire some day).

Powershell is dumb and bad because its GUI support gives me Visual Aids.

If there's another language that's going to be (a) consistently available on all my windows boxes without having to install a whole 'nother software package and (b) can give me GUIs so that I can give things to non-programmers and not have them stare at me blankly, pls let me know.
When I made my PowerShell/Forms abomination I made the black console emit a "doing thing..." prompt, so you could just do that.

Toshimo
Aug 23, 2012

He's outta line...

But he's right!

anthonypants posted:

When I made my PowerShell/Forms abomination I made the black console emit a "doing thing..." prompt, so you could just do that.

I unironically like this.

Pile Of Garbage
May 28, 2007



The Fool posted:

I've kinda felt that you shouldn't be doing anything with powershell that would require a GUI anyway. Your scripts should be written to be able to be ran headless, and if you need a GUI launcher type thing, do that in a different language.

One thousand times this. If you're building GUIs with PowerShell then you're doing it wrong. Conversely if you're writing scripts to run unattended and don't implement logging then you should be taken round back and shot.

SeaborneClink
Aug 27, 2010

MAWP... MAWP!
Someone at work sets $ErrorActionPreference = "Continue" and then fires blindly into the void. I asked them what the point was, if Step 4 is predicated on Steps 1-3 not failing, then of course Steps 4-6 aren't going to work. They kind of shrugged and said 'Then I get a full stack trace'

Well yes... But wouldn't you want the actual only error message that matters? Not a bunch of garbage because all the required foundational steps failed?

:iiam:

Pile Of Garbage
May 28, 2007



Yeah I hardly ever see people implement proper exception handling in scripts. Also input validation, especially checking for $null, which can have hilarious consequences if not done properly. For example, the Get-Mailbox command in Exchange PowerShell accepts $null for the Identity parameter and will just return all mailboxes. So if you run the following all your mailboxes will be disabled:

code:
$null | Get-Mailbox | Disable-Mailbox -Confirm:$false
You can see how this can backfire in a script if your logic is off.

Adbot
ADBOT LOVES YOU

anthonypants
May 6, 2007

by Nyc_Tattoo
Dinosaur Gum

cheese-cube posted:

Yeah I hardly ever see people implement proper exception handling in scripts. Also input validation, especially checking for $null, which can have hilarious consequences if not done properly. For example, the Get-Mailbox command in Exchange PowerShell accepts $null for the Identity parameter and will just return all mailboxes. So if you run the following all your mailboxes will be disabled:

code:
$null | Get-Mailbox | Disable-Mailbox -Confirm:$false
You can see how this can backfire in a script if your logic is off.
That's why I do -WhatIf and -Verbose until I don't get any errors :smuggo:

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply