Viewing entries tagged
active directory

PowerShell: Easy function for connecting to sessions on remote domain controllers

PowerShell: Easy function for connecting to sessions on remote domain controllers

One of the things I love doing with PowerShell is enabling my team to learn how to use it while providing tools that make specific or repetitive commands easy to use. 

Enter-PSSession is easy and straightforward to use, however it can be made even easier and used in a way to let users select from a list of servers, pick one, and then establish that connection.

Requirements

The Code

function Enter-DCSession {
    [cmdletbinding()]
    param(
        )
    $i = 0
    Write-Host "Enter PowerShell session on: ('q' exits to terminal)"`n -foregroundcolor $foregroundcolor
    foreach ($d in $ourDCs){
        if ($i -lt 10) {Write-Host "" $i -> $d.Name} else {Write-Host $i -> $d.Name}
        $i++
    
    }
   
    $enterSession = Read-Host "DC #?"
    While (!$adminCredential) { $adminCredential = Get-Credential -UserName 'domain\' -Message "Please input your admin account" }
if (($enterSession -ne "q") -and ($ourDCs.Name -contains $ourDCs[$enterSession].Name)) { 
        
        Write-Host "Connecting to remote session... type 'exit' to drop back to the local session."`n
        Enter-PSSession -ComputerName $ourDCs[$enterSession].Name -Credential $adminCredential
        
    } else {
        
        Write-Host `n'q or invalid option specified.' -ForegroundColor Red
        
    }
}

You can use this function in a module, and then call it via Enter-DCSession. It will then display a list of DCs stored in $ourDCs, and allow you to connect to one of them.

The logic in the script will only accept valid name selections as it compares the returned result to the names in the array $ourDCs.

if (($enterSession -ne "q") -and ($ourDCs.Name -contains $ourDCs[$enterSession].Name))

Come to think of it, I could actually just have the latter half of that if statement an it would work the same way. There's some cleanup I'll have to do :)

This script's logic can apply to a variety of things. You have a list of objects you want to take an action on in an array as a start, and then you use a foreach to do it. Using $i = 0 before the foreach, and then $i++ within the foreach, allows you to provide users a number to interact with that matches the number in the array. 

That is how this script takes numbers as an input that matches the name in the array.

I hope you found this helpful, and I'm always open to feedback!

-Ginger Ninja

 

PowerShell: Reset a user's AD password

PowerShell: Reset a user's AD password

I cant login

"My password isn't working!" ...  Can you reset it?" These words are typically heard at least once or twice a day by front line support at most places. No matter how much we try to inform and remind users about their expiring passwords, there will be those that forget to change it before it expires or end up locking themselves out one way or another (iPhone/Exchange anyone?)

AD functions in PowerShell

I have written a few AD functions in PowerShell. One of them gets information about a user and then passes that along to possible actions to take. The function I'm writing about is one of those actions. 

Requirements

  • Quest AD tools
  • Resolve-UserName function (will give the code for that below)
  • Set $foregroundColor to something you like so it looks pretty.

Actions!

What this function will do is take an account name (or ask you for one), and then give you 3 options.

  • Reset and flag for change
  • Reset and don't flag for a change
  • Just flag for reset on next logon (or unflag if already flagged)

Parameters

-actName (Account name to reset)

The Code

This is the code for the Resolve-Username function:

function Resolve-UserName {

    param ($Username, [switch]$Verbose=$false)

    if (!$Username) {
        $Username = Read-Host "Enter username" 
    }

    if($Verbose){ Write-Host "Attempting to resolve `"$Username`"..." }
    $ResolvedName = Get-QADUser $Username -IncludeAllProperties
    do{
        If ($ResolvedName.Length -gt 1) {
    
            Write-Host `n"Multiple users found for `"$Username`", please select the intended user."`n -ForegroundColor $foregroundColor
            
            $i = 0
    
            Foreach ($u in $ResolvedName) {
                Write-Host "$i -> " $u.Name
                $i++
            }
            $selUsr = Read-Host "Which #?"
            $ResolvedName = Get-QADUser $ResolvedName[$selUsr] -IncludeAllProperties
        }
        elseif($ResolvedName.Length -eq 0){
            Write-Host "User does not exist. Try again." -ForegroundColor Red
            $ResolvedName = Get-QADUser (Read-Host "Enter username") -IncludeAllProperties
        }
    }until($ResolvedName.length -eq 1)
    if($Verbose){ Write-Host "Resolved `"$Username`" to" $ResolvedName.SamAccountName }
    Return $ResolvedName
}

This is the code for the Reset-ADPassword function:

function Reset-ADPassword {

    param($actName)
    
    if(!$actName){$actName = Read-Host "Enter username"}
    $usrObj = Resolve-UserName $actName

    If ($usrObj) {    
        
        Write-Host `n"Password reset:" $usrObj.Name -ForegroundColor $foregroundColor
        
        Write-Host "1. Reset and flag"
        Write-Host "2. Reset and do not flag"
        Write-Host "3. Just flag (or unflag if flagged)"`n
        
    
        $psdOp = Read-Host "Please specify an option #" 
        Write-Host "Note: You will be prompted to confirm any option you select." -ForegroundColor $foregroundColor
    
        Switch ($psdOp)  {
    
            1 { 
                
                $resetTo = Read-Host "Reset password to" -AsSecureString
                $resetTo = $resetTo | ConvertFrom-SecureString
                $PlainTextPassword = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR( (ConvertTo-SecureString $resetTo) ))
                Set-QADUser $usrObj -UserPassword $PlainTextPassword -UserMustchangePassword $true -Credential $adminCredential -confirm }
    
            2 { 
                
                $resetTo = Read-Host "Reset password to" -AsSecureString
                $resetTo = $resetTo | ConvertFrom-SecureString
                $PlainTextPassword = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR( (ConvertTo-SecureString $resetTo) ))
                Set-QADUser $usrObj -UserPassword $PlainTextPassword -UserMustchangePassword $false -Credential $adminCredential -confirm }
    
            3 { 
                
                if ($usrObj.UserMustChangePassword -eq $true) {
                    Set-QADUser $usrObj -UserMustChangePassword  $false  -Credential $adminCredential -confirm }
                else { Set-QADUser $usrObj -UserMustchangePassword $true -Credential $adminCredential -confirm } }  
        }
    } 
}

Let me know if you have any questions or comments!

 

PowerShell: Get-DCDiag script for getting domain controller diagnostic information

PowerShell: Get-DCDiag script for getting domain controller diagnostic information

I wrote a script that will run DCDiag on domain controllers that you specify or all DCs in your environment. 

I will be working to improve the script as much as I can. This was a way for me to learn how advanced parameters and object building works in PowerShell.

The "all" and "full" options currently return an object that you can manipulate. The other options will do that soon once I update the script and test it more.

<#   
.SYNOPSIS   
   Display DCDiag information on domain controllers.
.DESCRIPTION 
   Display DCDiag information on domain controllers. $adminCredential and $ourDCs should be set externally.
   $ourDCs should be an array of all your domain controllers. This function will attempt to set it if it is not set via QAD tools.
   $adminCredential should contain a credential object that has access to the DCs. This function will prompt for credentials if not set.
   If the all dc option is used along side -Type full, it will return an object you can manipulate.
.PARAMETER DC 
    Specify the DC you'd like to run dcdiag on. Use "all" for all DCs.
.PARAMETER Type 
    Specify the type of information you'd like to see. Default is "error". You can specify "full"           
.NOTES   
    Name: Get-DCDiagInfo
    Author: Ginger Ninja (Mike Roberts)
    DateCreated: 12/08/2015
.LINK  
    https://www.gngrninja.com/script-ninja/2015/12/29/powershell-get-dcdiag-commandlet-for-getting-dc-diagnostic-information      
.EXAMPLE   
    Get-DCDiagInfo -DC idcprddc1 -Type full
    $DCDiagInfo = Get-DCDiagInfo -DC all -type full -Verbose
#>  
    [cmdletbinding()]
    param(
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [String]
        $DC,
        
        [Parameter()]
        [ValidateScript({$_ -like "Full" -xor $_ -like "Error"})]
        [String]
        $Type,
        
        [Parameter(Mandatory=$false,ValueFromPipeline=$false)]
        [String]
        $Utility
        )
    
    try {
        
    if (!$ourDCs) {
        
        $ourDCs = get-QADComputer -computerRole 'DomainController'
    
    }
    
    if (!$adminCredential) {
        
        $adminCredential = Get-Credential -Message "Please enter Domain Admin credentials"
        
    }
    
    Switch ($dc) {
    
    {$_ -eq $null -or $_ -like "*all*" -or $_ -eq ""} {
    
        Switch ($type) {  
            
        {$_ -like "*error*" -or $_ -like $null} {  
             
            [array]$dcErrors = $null
            $i               = 0
            
            foreach ($d in $ourDCs){
            
                $name = $d.Name    
                
                Write-Verbose "Domain controller: $name"
                
                Write-Progress -Activity "Connecting to DC and running dcdiag..." -Status "Current DC: $name" -PercentComplete ($i/$ourDCs.Count*100)
                
                $session = New-PSSession -ComputerName $d.Name -Credential $adminCredential
                
                Write-Verbose "Established PSSession..."
                
                $dcdiag  = Invoke-Command -Session $session -Command  { dcdiag }
                
                Write-Verbose "dcdiag command ran via Invoke-Command..."
            
                if ($dcdiag | ?{$_ -like "*failed test*"}) {
                    
                    Write-Verbose "Failure detected!"
                    $failed = $dcdiag | ?{$_ -like "*failed test*"}
                    Write-Verbose $failed
                    [array]$dcErrors += $failed.Replace(".","").Trim("")
            
                } else {
                
                    $name = $d.Name    
                
                    Write-Verbose "$name passed!"
                    
                }
                
                
                Remove-PSSession -Session $session
                
                Write-Verbose "PSSession closed to: $name"
                $i++
            }
            
            Return $dcErrors
        } 
            
        {$_ -like "*full*"}    {
            
            [array]$dcFull             = $null
            [array]$dcDiagObject       = $null
            $defaultDisplaySet         = 'Name','Error','Diag'
            $defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet',[string[]]$defaultDisplaySet)
            $PSStandardMembers         = [System.Management.Automation.PSMemberInfo[]]@($defaultDisplayPropertySet)
            $i                         = 0
            
            foreach ($d in $ourDCs){
                
                $diagError = $false
                $name      = $d.Name
                
                Write-Verbose "Domain controller: $name"
                
                Write-Progress -Activity "Connecting to DC and running dcdiag..." -Status "Current DC: $name" -PercentComplete ($i/$ourDCs.Count*100)
                
                $session = New-PSSession -ComputerName $d.Name -Credential $adminCredential
                
                Write-Verbose "Established PSSession..."
                
                $dcdiag  = Invoke-Command -Session $session -Command  { dcdiag }
                
                Write-Verbose "dcdiag command ran via Invoke-Command..."
                
                $diagstring = $dcdiag | Out-String
                
                Write-Verbose $diagstring
                if ($diagstring -like "*failed*") {$diagError = $true}
                
                $dcDiagProperty  = @{Name=$name}
                $dcDiagProperty += @{Error=$diagError}
                $dcDiagProperty += @{Diag=$diagstring}
                $dcO             = New-Object PSObject -Property $dcDiagProperty
                $dcDiagObject   += $dcO
                
                Remove-PSSession -Session $session
                
                Write-Verbose "PSSession closed to: $name"
                
                $i++
            }
            
            $dcDiagObject.PSObject.TypeNames.Insert(0,'User.Information')
            $dcDiagObject | Add-Member MemberSet PSStandardMembers $PSStandardMembers
            
            Return $dcDiagObject
        
            }
        
        }
         break         
    }
   
   
    {$_ -notlike "*all*" -or $_ -notlike $null} {
   
        Switch ($type) {
        
        {$_ -like "*error*" -or $_ -like $null} {
        
            if (Get-ADDomainController $dc) { 
    
                Write-Host "Domain controller: " $dc `n -foregroundColor $foregroundColor
            
                $session = New-PSSession -ComputerName $dc -Credential $adminCredential
                $dcdiag  = Invoke-Command -Session $session -Command  { dcdiag }
       
                if ($dcdiag | ?{$_ -like "*failed test*"}) {
                
                    Write-Host "Failure detected!"
                
                    $failed = $dcdiag | ?{$_ -like "*failed test*"}
                
                    Write-Output $failed 
                
                } else { 
                
                    Write-Host $dc " passed!"
                
                }
                    
            Remove-PSSession -Session $session       
            } 
        }
        
        {$_ -like "full"} {
            
            if (Get-ADDomainController $dc) { 
    
                Write-Host "Domain controller: " $dc `n -foregroundColor $foregroundColor
            
                $session = New-PSSession -ComputerName $dc -Credential $adminCredential
                $dcdiag  = Invoke-Command -Session $session -Command  { dcdiag }
                $dcdiag     
                    
                Remove-PSSession -Session $session       
            }     
                
        }
        
    }
    
    }
    
    }
    
    }
    
    Catch  [System.Management.Automation.RuntimeException] {
      
        Write-Warning "Error occured: $_"
 
        
     }
    
    Finally { Write-Verbose "Get-DCDiagInfo function execution completed."}

You can run this as a script or as a function within a script depending on your needs. Let me know if you have any feedback!