Pablo's Powershell Pow-Wow

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

  1. looktall

    looktall Working Class Doughnut

    Joined:
    Sep 17, 2001
    Messages:
    26,669
    Code:
    Import-Csv .\users.csv |
    foreach {
    $Params = @{
     password = (ConvertTo-SecureString $_.Password -AsPlainText -Force)
     identitiy= $_.username
    }
    $logmessage = @{
    path = ".\Log.txt"
    value = "$(Get-Date -Format "yyyy-MM-dd - HH:mm"): Reset the password for account $($_.Username) to $($_.Password)"
    }
     Set-ADAccountPassword @params -reset -newpassword
    add-content @logmessage
    }
    
    or something
     
  2. freaky_beeky

    freaky_beeky Member

    Joined:
    Dec 2, 2004
    Messages:
    1,172
    Location:
    Brisbane
    Here is my something

    Code:
    #requires -module ActiveDirectory
    
    <#
    .Synopsis
       This script supports two parameter sets, and sets the password of the related user.
    
       This is done either via -Username and -Password parameters
       or
       via a CSV file with Username and Password columns
       Ensure no blank lines are in your CSV for best results
    .EXAMPLE
       powershell -file .\reset_password_bulk.ps1 -Verbose -Path .\userpass.csv
    
       Validates the CSV file, and ensure that it contains Usernames and Passwords
       Using implied credentials sets each Username to its related Password (if it meets the requirements) via implied credentials 
    .EXAMPLE
       powershell -file .\reset_password_bulk.ps1 -Username User -Password C0mplicatedPa$$
    
       Using implied credentials, resets the passford for 'User' to 'C0mplicatedPa$$'
    .NOTES
       This script does not support a variable log path, but could be easily implemented with an additional parameter
    .FUNCTIONALITY
       Resets users passwords entered via clear text
    #>
    [CmdletBinding(DefaultParameterSetName='UserPass', 
                    SupportsShouldProcess=$true, 
                    PositionalBinding=$true,
                    HelpUri = 'http://www.tubgirlc.om',
                    ConfirmImpact='Low')]
    Param
    (
        # Username of user to reset
        [Parameter(Mandatory=$true, 
                    ValueFromPipeline=$true,
                    ValueFromPipelineByPropertyName=$true, 
                    ValueFromRemainingArguments=$true, 
                    Position=0,
                    HelpMessage='Enter Username of user to reset',
                    ParameterSetName='UserPass')]
        [ValidateNotNullOrEmpty()] #Ensure not null or empty
        [ValidatePattern('^[\w\._-]{1,20}$')] #Ensure user is between 1 and 20 chars, only including letters,numbers,dots,dashes,underscores
        [string[]] 
        $Username,
    
        # Password to reset for the user (cleartext)
        [Parameter(Mandatory=$true, 
                    ValueFromPipeline=$true,
                    ValueFromPipelineByPropertyName=$true, 
                    ValueFromRemainingArguments=$true, 
                    Position=1,
                    HelpMessage='Enter the plaintext password to set the users password to',
                    ParameterSetName='UserPass')]
        [ValidateNotNullOrEmpty()] #Ensure not null or empty
        [ValidateLength(8,50)] #Ensure password is at least 8 chars long, max of 50
        [string[]]
        $Password,
    
        # Path to CSV to reset list of users and passwords (cleartext)
        [Parameter(Mandatory=$true, 
                    ValueFromPipeline=$true,
                    ValueFromPipelineByPropertyName=$true, 
                    ValueFromRemainingArguments=$true, 
                    Position=0,
                    ParameterSetName='CSV')]
        [ValidateNotNullOrEmpty()] #Ensure not null or empty
        [ValidateScript({if(Test-Path -Path $_ -Filter *.csv){$true}else{throw "Invalid CSV path, '$_' provided, please enter a valid path and rerun script"}})]
        [string]
        $Path
    )
    #Start Logging
    Start-Transcript -Path "$env:TEMP\$(Get-Date -Format FileDateTime)_set_user_pass.log"
    
    #Variable Declaration
    $csvContent = $null
    $csvProperties = $null
    #End Variable DeclaraTION
    
    #if the script was provided a CSV convert that to Username and Password variables
    if($PSCmdlet.ParameterSetName -eq 'CSV')
    {
        try
        {
            Write-Verbose -Message "Detected CSV path as '$Path', attempting to import"
            $csvContent = Import-Csv -Path $Path -ErrorAction Stop
            $csvProperties = $csvContent | Get-Member -MemberType Properties -ErrorAction Stop
            #if the csv contains Password and UserName fileds, use it
            if($csvProperties.Name -contains 'Password' -and $csvProperties.Name -contains 'UserName')
            {
                Write-Verbose -Message 'Valid CSV Detected'
                $Username = $csvContent.Username #extract Usernames into variable
                $Password = $csvContent.Password #extract Passwords into variable
                $csvContent = $null #clear CSV content 
                Write-Verbose -Message 'Successfully imported CSV'
            }
            else #throw error, bad CSV
            {
                Write-Verbose -Message 'Invalid CSV Detected'
                throw 'Invalid CSV detected, Username and Password columns not detected'
            }
        }
        catch
        {
            Write-Warning -Message "Failed importing CSV from path, '$path' with error, $($Error[0].Exception.Message)"
        }
    }
    
    #iterate through each user and set the password
    for ($i = 0; $i -lt $Username.Count; $i++)
    { 
        $adUser = $null #clear variable for each iteration of loop
        $securePassword = $null
        try
        {
            Write-Verbose -Message "Attempting to find user, '$($Username[$i])' in current AD domain, '$env:USERDNSDOMAIN'"
            $adUser = Get-ADUser -Identity $($Username[$i]) -Server $env:USERDNSDOMAIN -AuthType Negotiate -ErrorAction Stop
            Write-Verbose -Message 'Successfully retrieved AD User'
        }
        catch
        {
            Write-Warning -Message "Failed attempting to find user, '$($Username[$i])' in AD Domain, '$env:USERDNSDOMAIN', with error, $($Error[0].Exception.Message)"
            Write-Warning -Message 'Skipping user'
            $adUser = $null
        }
    
        #if the user was found in AD, attempt password reset
        if($adUser)
        {
            try
            {
                Write-Verbose -Message "Attempting to reset password for user, '$($Username[$i])'"
                $securePassword = ConvertTo-SecureString $Password[$i] -AsPlainText -Force
                $adUser | Set-ADAccountPassword -Reset -NewPassword $securePassword -ErrorAction Stop
                Write-Verbose -Message 'Successfully reset password'
            }
            catch
            {
                Write-Warning -Message "Failed attempting to reset password for user, '$($Username[$i])' with error, $($Error[0].Exception.Message)"
            }
        }
        else
        {
            Write-Verbose -Message "Skipping user, '$($Username[$i])'"
        }
        $Password[$i] = $null #clear password after user
    }
    
    #Stop Logging
    Stop-Transcript -ErrorAction SilentlyContinue
    
    Just needed to do something that was actually achievable today...

    Works with CSV input or -Username -Password

    Logging is to user temp directory with ISO 8601 timestamp prefix.
     
    miicah and PabloEscobar like this.
  3. looktall

    looktall Working Class Doughnut

    Joined:
    Sep 17, 2001
    Messages:
    26,669
    that help URI though.
     
    freaky_beeky and miicah like this.
  4. miicah

    miicah Member

    Joined:
    Jun 3, 2010
    Messages:
    7,508
    Location:
    Mount Cotton, QLD
    I appreciate the effort, but I just wanted to know if there was any performance penalty or issue with evaluating cmdlets inside another cmdlet rather than using the pipeline (possibly for when working with massive datasets).
     
  5. looktall

    looktall Working Class Doughnut

    Joined:
    Sep 17, 2001
    Messages:
    26,669
    your code has actually solved an issue i was having with a script i've been writing.
    i was using "write-host" to inform the person running the script what steps are happening.
    I also wanted to save the output to a log file, but of course write-host goes nowhere but the console.

    Seeing "start-transcript" in your code led me down a rabbit hole that eventually resulted in me changing "write-host" into "Write-output" and being able to have my cake and eat it too.

    thanks
     
  6. elvis

    elvis Old school old fool

    Joined:
    Jun 27, 2001
    Messages:
    45,382
    Location:
    Brisbane
    Marry me.
     
  7. freaky_beeky

    freaky_beeky Member

    Joined:
    Dec 2, 2004
    Messages:
    1,172
    Location:
    Brisbane
    I was of the understanding that position was taken? And alas, I do not believe polyamory is legal in this country.


    Glad I could be of some assistance.

    Write-Output is dangerous, because it's actually _output_, so if it's in a function, it counts towards the "return".
    I would recommend using Write-Verbose instead and try one of the following:
    • $verbosePreference = 'continue' in your script (to ensure all verbose messages are printed)
    • Write-Verbose -Message "your message here" -Verbose (to force the output of the message regardless of the $verbosePreference
    • Use advanced cmdlets and call your script with -Verbose (and perhaps just force the needed messages via the above method)
    But your method works fine too :)
     
    miicah likes this.
  8. looktall

    looktall Working Class Doughnut

    Joined:
    Sep 17, 2001
    Messages:
    26,669
    i'm only doing simple things at the moment. i reckon it will be a while before i figure out how to write functions, but i'll keep it in mind.
     
  9. looktall

    looktall Working Class Doughnut

    Joined:
    Sep 17, 2001
    Messages:
    26,669
    a new year, a new question.


    I'm splatting parameters and it's working fine, but i want to know if i can do the below.

    Code:
    $GroupParameters = @{
    MemberOf = $UniversalGroups, "$($_.Office) DL", "$($_.Office) Staff - SG"
    Some context....

    the $UniversalGroups variable was defined already at the top of the script. it contains several DL's and SG's that all users are added to (eg. All Staff - SG, etc).
    $_.Office is a column in an imported csv file that refers to their home site.
    My intention is for a newly created user to be added to universalgroups and also the already existing DL and SG for their home site.

    For example, if $_.Office = Perth.
    I'm thinking that "$($_.Office) DL" will be seen as "Perth DL" and the user will be added to All Staff - SG, Perth DL, etc.
    Is my thinking correct?

    This will be one less step we need to do when creating a new user.

    The command i plan to run with the above is
    Code:
    Add-ADPrincipalGroupMembership @GroupParameters
    The current code is
    Code:
    $GroupParameters = @{
    MemberOf = $UniversalGroups
    Which i run with the above command with great success.

    I hope all this makes sense.

    I've had several gins before typing apologies if it makes no sense at all. :)
     
  10. freaky_beeky

    freaky_beeky Member

    Joined:
    Dec 2, 2004
    Messages:
    1,172
    Location:
    Brisbane
    I want to help, but I just can't work it out from your question.

    what does $_ pertain to in this context? Are you in a foreach-object loop?

    Would you be comfortable sharing a bit more?

    My guess is that MemberOf cannot handle an array of strings, and instead requires an adobject (as can be seen in the help, here: https://docs.microsoft.com/en-us/powershell/module/activedirectory/add-adprincipalgroupmembership)

    And you aren't ensuring that.

    my guess is it's something like this:

    userdetails.csv:
    Code:
    Office,Banana
    Perth,TRUE
    Brisbane,TRUE
    Hobart,FALSE
    Canberra,FALSE
    Melbourne,FALSE
    Sydney,FALSE
    
    Script
    Code:
    [string[]]$UniversalGroups = 'DL_Uni_group_01','DL_Uni_group_02','DL_Uni_group_nonConformist','SG_uni_Hookers_Blow'
    
    $UnknownObject = Import-Csv -Path C:\temp\userdetails.csv
    
    $UnknownObject | Format-Table
    
    $UnknownObject | ForEach-Object{
    
        $GroupParameters = @{
        MemberOf = $UniversalGroups, "$($_.Office) DL", "$($_.Office) Staff - SG"
        Identity = $env:USERNAME
        }
    }
    
    $GroupParameters
    
    Output
    Code:
    Office    Banana
    ------    ------
    Perth     TRUE 
    Brisbane  TRUE 
    Hobart    FALSE 
    Canberra  FALSE 
    Melbourne FALSE 
    Sydney    FALSE 
    
    
    
    Name     Value                                                                                                         
    ----     -----                                                                                                         
    MemberOf {DL_Uni_group_01 DL_Uni_group_02 DL_Uni_group_nonConformist SG_uni_Hookers_Blow, Sydney DL, Sydney Staff - SG}
    
    This code "works" for me, in that it fails with your group names because they don't exist in my AD.

    However when I use the following:

    Code:
    [string[]]$UniversalGroups = 'DL_Uni_group_01','DL_Uni_group_02','DL_Uni_group_nonConformist','SG_uni_Hookers_Blow'
    
    $UnknownObject = Import-Csv -Path C:\temp\userdetails.csv
    
    $UnknownObject | Format-Table
    
    $UnknownObject | ForEach-Object{
    
        $GroupParameters = @{
        MemberOf = 'Domain Admins','Domain Users'
        Identity = $env:USERNAME
        }
    }
    
    $GroupParameters
    
    Add-ADPrincipalGroupMembership @GroupParameters -WhatIf
    
    What if gives me the following:
    Code:
    What if: Adds all the specified member(s) to the specified group(s).
    
     
  11. looktall

    looktall Working Class Doughnut

    Joined:
    Sep 17, 2001
    Messages:
    26,669
    I gave up on trying to nut it out and ended up using Add-ADGroupMember instead and split the command into two separate commands.
    one to add the DL and another to add the SG.

    It does what i need and doesn't add much to the script.

    thanks for the help though.
     
  12. gav1ski

    gav1ski Member

    Joined:
    Aug 9, 2001
    Messages:
    163
    Location:
    Sydney
    Needing help for a script I am trying to put together, I want to identify the process on a server using > 90% CPU and kill it. The kill bit is easy but I have not been able to find any way to get the CPU % as Get-Process returns CPU time not CPU % along with the process ID. get-Counter is a possibility but I need the process ID out not a name as the server is running several processes with the same name and only 1 is causing the problem so only 1 needs to be killed.

    Background it that I want to automate a fix of a rarely occurring issue (< once every 6 months) where an IIS worker process would flat line and bring the web site down. The manual process is to remote into the server, identify the process and kill it, this takes > 10 mins as the machine is crapping itself. So as it's hosted in AWS, automating a script on a cloud watch event is trivial and would speed up the resolution of the issue while still alerting us that it has happened.
     
  13. millsy

    millsy Member

    Joined:
    Mar 31, 2007
    Messages:
    13,596
    Location:
    Brisbane
    You'd wanna be careful with that, because it'd be pretty easy to kill something random...
     
  14. chip

    chip Member

    Joined:
    Dec 24, 2001
    Messages:
    3,969
    Location:
    Pooraka Maccas drivethrough
    have you not had any success using the IIS app pool CPU settings to control this? I'd look at using that to kill a worker process after it's exceeded X% CPU for Y duration. I'd be surprised if using get-process and looking at total CPU time is the most effective solution to this problem.

    comedy option is get the application code fixed so it doesn't hang

    edit: IIS's appcmd.exe has a shitload of options for getting data about IIS worker processes too
     
    Last edited: Feb 24, 2020
    millsy and freaky_beeky like this.
  15. gav1ski

    gav1ski Member

    Joined:
    Aug 9, 2001
    Messages:
    163
    Location:
    Sydney
    Planning to limit it to w3c processes so should not be a problem

    Just had a look, while I can list the worker processes I can't get CPU data at the same time. As for actually fixing it, I would love to but since it happens so rarely I have never been able to pin down the instigator as all of our request logging to identify problem pages records the time after the page is sent back to the user and when the whole system is running slow every page reports slow response time so I have not found the cause yet.

    As for the limit I have tried it a couple of times and it has never worked reliably I have set it to 90% (90,000) and had the process happily using 95% and not getting killed. It is affected by the number of cores I think so the 90% is not actually 90% to IIS.
     
  16. chip

    chip Member

    Joined:
    Dec 24, 2001
    Messages:
    3,969
    Location:
    Pooraka Maccas drivethrough
    are you able to use taskmanager's analyze wait chain function, or process explorer, to poke deeper and find out what threads are actually holding up the worker process?

    TBH it sounds like you've done all the sorts of things I'd do, so I'm just spitballing things here.
     
  17. gav1ski

    gav1ski Member

    Joined:
    Aug 9, 2001
    Messages:
    163
    Location:
    Sydney
    The main problem is that it has only ever happened in production and never in test, so when users are locked our of production the main focus is to get them back in as fast as possible hence the need for the script automation. Ideally I would also like to do a process dump before killing the process to see if there is anything I can spot that is causing the issue, as for all I know it may be some IIS crap out that is causing it and nothing to do with our code.
     
  18. looktall

    looktall Working Class Doughnut

    Joined:
    Sep 17, 2001
    Messages:
    26,669
    What's different on the server between production and test?

    Do you not have monitoring on the server?
    Something that will not only alert you that a process is hung but which process it was and what called it.
     
  19. gav1ski

    gav1ski Member

    Joined:
    Aug 9, 2001
    Messages:
    163
    Location:
    Sydney
    Almost identical, in IIS configuration and app pool settings, the main difference is that test runs a lot more while production is dedicated to just the IIS components. We monitor both environments using NewRelic APM and infrastructure that gives us the ability to identify performance issue before our customers spot them most of the time, but once the CPU flat lines everything runs slow and it is almost impossible to to find the cause in all of the slow requests.

    I ended up creating this as so far there is only 1 problem application pool so I am getting the process ID from the IIS appcmd then performing a dump, killing the process, uploading the result to S3 and finally removing the dump file to save space. As I said this will be automated from an AWS cloud watch event to make recovery faster and give me a dump file to dig through to hopefully find the actual cause.

    An a side note I also came across this https://blog.jetbrains.com/dotnet/2017/03/14/using-dotpeek-figure-iis-crashed/ to analyse the dump file against our code base.

    Code:
    $workerProcs = C:\Windows\system32\inetsrv\appcmd list wp
    foreach ($wp in $workerProcs)
    {
        if ($wp -like "*App Pool name*")
        {
            $params = $wp.split(" ")
            $procID = $params[1].Replace("""", "")
            $WPName = $params[2].Replace("(applicationPool:", "").Replace(")","")
            $dateName = Get-Date -Format "yyyyMMdd"
            $file = "c:\temp\$dateName$WPName.dmp"
            C:\Procdmp\procdump64.exe $procID -ma $file
            Stop-Process -Id $procID -Force
            #upload to s3
            Import-Module "C:\Program Files (x86)\AWS Tools\PowerShell\AWSPowerShell\AWSPowerShell.psd1"
    
            $bucket = 'S3BucketName'
            $AKey   = ''
            $SKey   = ''
            $region = 'ap-southeast-2'
    
            Initialize-AWSDefaultConfiguration -AccessKey $AKey -SecretKey $SKey -Region $region
            write-s3object -BucketName $bucket -File $file -key "$dateName$WPName.dmp"
            Remove-Item -Path $file
        }
    }
     
  20. sic_vl2000

    sic_vl2000 Member

    Joined:
    Dec 13, 2004
    Messages:
    998
    I can't believe in 2020 companies are still creating / disabling user accounts in AD with zero automation. Time to start creating something for at least AD / Exchange.
     

Share This Page

Advertisement: