1. OCAU Merchandise is available! Check out our 20th Anniversary Mugs, Classic Logo Shirts and much more! Discussion in this thread.
    Dismiss Notice

Pablo's Powershell Pow-Wow

Discussion in 'Business & Enterprise Computing' started by PabloEscobar, Sep 18, 2014.

  1. colmaz

    colmaz Member

    Joined:
    Jan 8, 2007
    Messages:
    453
    Location:
    Perth, WA
    It's when I review past me's code, that I remember I should comment my code more often.

    As mentioned above, my preferred approach (when I remember) is to write out what the script does as pseudocode comments. Then I start writing the actual code around it.

    My top tips:
    • Be consistent in all things as it makes it easier to follow
      • Refactor older code if you have to
    • Use variables that make sense
      • $object is bad, $AD_User is better
    • Use of Hungarian notation is typically avoided
      • $strThing, $intCound, etc.
      • It gets kind of hard after a while when everything becomes $objThis, $objThat
    • Use consistent casing
      • snake_case vs Title_Case vs camel_Case, etc.
    • Have a look at PS Script Analyzer and take it's recommendations with a pinch of salt
    I'd have a look at the Practice and Style guide (it most likely covers the above) but work blocks GitHub.
     
  2. bart5986

    bart5986 Member

    Joined:
    Jan 31, 2006
    Messages:
    5,293
    Location:
    Brisbane
    Seeing as we seem to work in the same job, I'm curious, what are you planning to do with your powershell scripts at logon?

    If only there was a script to install printer drivers without administrative access :lol:
     
  3. miicah

    miicah Member

    Joined:
    Jun 3, 2010
    Messages:
    7,665
    Location:
    Mount Cotton, QLD
    Puts a logon record in our DB, updates PaperCut config files, make sure all language packs are installed (although I don't think I need this any more), remove drive mappings (for normal and exam accounts).
     
  4. bart5986

    bart5986 Member

    Joined:
    Jan 31, 2006
    Messages:
    5,293
    Location:
    Brisbane
    Oh ok thanks for the information, I've been planning to do a logon record for a while.
     
  5. Gunna

    Gunna Member

    Joined:
    Dec 25, 2001
    Messages:
    7,946
    Location:
    Brisbane
    I am trying to create a termination script and some of our users may have Admin accounts.

    Their standard accounts are in format AB1234 and admin accounts are similar to AB1234.admin.
    I need a dynamic menu to return 1 or more results

    Testing my script if there are 2 AD objects it is fine but if there is 1 I get an error. The menu code I found online and isnt mine.

    Code:
    $TeamMemberNumber = Read-Host "Please Enter the Team Members username or team member ID"
    $UserQuery = Get-ADUser -Filter "SamAccountName -like '*$TeamMemberNumber*'" -server "%domain controller"
    echo "`n"
    Echo "These were the users found with AD Username like $TeamMemberNumber :"
    echo "`n"
    $menu = @{}
    for ($i=1;$i -le $UserQuery.count; $i++) {
        Write-Host "$i. $($UserQuery[$i-1].UserPrincipalName)"
        $menu.Add($i,($UserQuery[$i-1].UserPrincipalName))
        }
    Echo "`n"
    [int]$ans = Read-Host 'Enter selection of account to terminate'
    $selection = $menu.Item($ans)
    $AUSADuser = Get-ADUser -filter "UserPrincipalName -eq '$selection'"-properties MemberOf, Description, manager -server "%DC%"
    $Firstname = $AUSADuser.GivenName
    $LastName = $AUSADuser.surname
    $Verify = Read-Host "Please verify new hires name is: $FirstName $Lastname before proceeding. Press Y to proceed or any key to cancel"
    if ($verify -eq "Y")
    {Write-host "Script is proceeding"}
    else
    {write-host "BROKEN!!!!"}
    I get this error if there is a single item to return
    Any ideas on a dynamic menu that allows a single result?
     
    Last edited: Sep 16, 2021
  6. looktall

    looktall Working Class Doughnut

    Joined:
    Sep 17, 2001
    Messages:
    26,893
    not a clue but in my exit script i have the person running it enter the samaccount of the person to be exited and then the script generates a bunch of variables (most of which i have removed in the example below) before asking the person to confirm if they have the right account.

    Code:
    $SAMAccountName = Read-Host 'Account name to disable'
    
    # Get the properties of the account and set variables
    $user = Get-ADuser $SAMAccountName -properties canonicalName, distinguishedName, displayName, Mail
    $DistinguishedName = $user.distinguishedName
    $CanonicalName = $user.canonicalName
    $DisplayName = $user.displayName
    $mail = $user.mail
    
    <...Bunch of Stuff...>
    <# --- Confirm correct account to exit --- #>
    
    $Message = "This will exit the user account for $($CanonicalName)."
    $Question = "Are you sure you wish to continue?"
    $Choices = "&Yes", "&No"
    $Decision = $Host.UI.PromptForChoice($Message, $Question, $Choices, 1)
    if ($Decision -eq 0) {
    write-host "Exiting User..."}
    else {
    write-host "Aborting Script. No changes have been made."
    exit}
     
  7. Gunna

    Gunna Member

    Joined:
    Dec 25, 2001
    Messages:
    7,946
    Location:
    Brisbane
    Cheers, I wanted it to do a search based on username or Team member ID to display all accounts as a reminder that this person may have an admin account we forgot about.

    There are reasons where sometimes the user needs a local admin account for a specific machine or could be an I.T team member who has an admin account so this would serve as validation of their accounts.
     
    looktall likes this.
  8. miicah

    miicah Member

    Joined:
    Jun 3, 2010
    Messages:
    7,665
    Location:
    Mount Cotton, QLD
    It's because PowerShell returns the single user object instead of an array of objects with 1 item if it only finds one result. I can't find where this is documented, but I have replicated your problem on my end.

    Get-Member for the single AD object still exposes the Count method, but it just returns nothing. So I did this:

    Code:
    if ($UserQuery.Count) {
        for ($i=1;$i -le $UserQuery.count; $i++) {
            Write-Host "$i. $($UserQuery[$i-1].UserPrincipalName)"
            $menu.Add($i,($UserQuery[$i-1].UserPrincipalName))
            }
    }
    else {
        Write-Host "1. $UserQuery.UserPrincipalName)"
        $menu.Add(1, $UserQuery.UserPrincipalName)
    }
    It's not very flexible, but the only use cases you have for this are 0 results (not handled), 1 result (handled) or more than 1 (handled).
     
    Gunna likes this.
  9. Gunna

    Gunna Member

    Joined:
    Dec 25, 2001
    Messages:
    7,946
    Location:
    Brisbane
    Good catch and adding [array] to before the variable that runs the Get-ADuser query forces it to use an array, even if a single entry is returned. That solves my issue
     
    miicah likes this.
  10. miicah

    miicah Member

    Joined:
    Jun 3, 2010
    Messages:
    7,665
    Location:
    Mount Cotton, QLD
    Is there a way to write this without the doubling up of the variable?

    Code:
    if ($test.errors.message) { return $test.errors.message }
    Basically just check if the return value has an error message in it or not and then return said error message.
     
  11. BBRadar

    BBRadar Member

    Joined:
    Jan 4, 2010
    Messages:
    12
    You can try assign the value to a new variable in the condition statement.
    Code:
    if ($errorsFound = $test.errors.message) { return $errorsFound }
    Side note: If you are implementing error handling, then there is an $error array with errors from that session (index 0 being the most recent)
    You can use
    Code:
    Try {"do something here"} catch {"handle the error"}
    eg.
    Code:
    Try {get-process -bogusParameter -erroraction silentlycontinue} catch {add-content -path .\logfile.log -value "$(get-date)`tThis happenned: $($Error[0].exception.message)"}
    Will try and fail to get-process, so the catch statement will run and in this case (log the error to file).
     
    miicah likes this.
  12. randomman

    randomman Member

    Joined:
    Oct 21, 2007
    Messages:
    5,279
  13. miicah

    miicah Member

    Joined:
    Jun 3, 2010
    Messages:
    7,665
    Location:
    Mount Cotton, QLD
    I have another one.

    What's the nicest looking way to form a large API url? I tried splatting it to the body of the API request but realised that wouldn't work because it's a GET request.

    I need to have

    Code:
    https://api.com.au/rest-v1/general/addUser/?token= &username= &firstname= &surname= &email= &shortcode= &security= &send=
    Which looks quite messy in all the ways I can recall doing string concatenation.

    EDIT: Maybe a custom PSobject? nah beacuse I will be passing to a function
     
    Last edited: Dec 9, 2021
  14. Ogre

    Ogre Member

    Joined:
    Aug 13, 2003
    Messages:
    2,454
    Location:
    Sydney, Australia
    Hey All,

    Hopefully this is simple and I am just missing something obvious. I am using this to get a list of the DHCP scopes I need to export.

    Code:
    $scopes = Get-DhcpServer4Scope -ComputerName "dhcpserver.some.domain.com" | Where-Object {$_.Name -like "UniqueIdentifier*"}
    Then I use this to join them all together

    Code:
    $scopeIds = $scopes.ScopeID -join ","
    The output is basically 123.123.123.44,123.123.123.55,123.123.123.66 etc

    Finally using this to export all the scopes

    Code:
    Export_DhcpServer -ComputerName "dhcpserver.some.domain.com" -File "C:\Folder\export.xml -ScopeId $scopeIds
    Then I get an error saying it cannot convert 123.123.123.44,123.123.123.55,123.123.123.66 to type "System.Net.IPAddress[]"

    If I manually type out the IP addresses in the same format it works, but not from within the variable.

    Any ideas?
     
  15. OP
    OP
    PabloEscobar

    PabloEscobar Member

    Joined:
    Jan 28, 2008
    Messages:
    14,678
    It's all about the Objects :).

    Code:
    $scopes = Get-DhcpServerv4Scope | Where-object {$_.Name -like "Blah*"}
     $scopes.ScopeId | Get-Member
    $scopes.scopeId returns a TypeName: System.Net.IPAddress Object

    Code:
    $scopes = Get-DhcpServerv4Scope | Where-object {$_.Name -like "Comms*"}
    $scopeIds = $scopes.ScopeID -join "," | Get-Member
    
    $scopeIds is now TypeName: System.String

    ---

    Export-DHCP server wants IP addresses for -scopeId, $scopes.scopeId are already IP addresses, so we don't need the middle line.

    Code:
    $scopes = Get-DhcpServerv4Scope | Where-object {$_.Name -like "Blah*"}
    export-dhcpserver -File "C:\support\DHCPExport.xml" -ScopeId $scopes.ScopeId
    Should do get you your XML file.
     
  16. Ogre

    Ogre Member

    Joined:
    Aug 13, 2003
    Messages:
    2,454
    Location:
    Sydney, Australia
    haha yes it did thank you
     
  17. Optimus.

    Optimus. Member

    Joined:
    Jun 27, 2002
    Messages:
    6,732
    Just quietly, I've been loving Powershell Universal lately at work. I've used it to become a repo, moved Windows Task Scheduler tasks over to it and also using it to host a couple of APIs (which lets me move common data requests to simple invoke-webrequests in my scripts. You get an awful lot of functionality in the free version.
     
  18. Ogre

    Ogre Member

    Joined:
    Aug 13, 2003
    Messages:
    2,454
    Location:
    Sydney, Australia
    How many servers/device are you managing with it?
     
  19. Optimus.

    Optimus. Member

    Joined:
    Jun 27, 2002
    Messages:
    6,732
    Mmm... I don't use Powershell like that. My scripts look much closer to C# than PS!
    I do manage to handle corp-wide integrations with it so for me it's more about 'how many users are going to have a bad day if this goes wrong'.
     
  20. 7nothing

    7nothing Member

    Joined:
    Feb 15, 2002
    Messages:
    1,587
    Location:
    Brisbane
    When you're working with network switches and think $Switch is a reasonable variable to use, but it isn't: https://docs.microsoft.com/en-us/po...utomatic_variables?view=powershell-7.2#switch

    Thing is, I was using a switch {} after I was done with the variable $Switch, but for some f'd up reason the switch {} just didn't get evaulated if it was in a function (would work find if I pasted it into console that already had $Switch declared).
     

Share This Page

Advertisement: