≡ Menu

This post will provide a script that you can use to perform password reset for multiple Active Directory user accounts at one go. This you can perform irrespective of the user account’s location in Active Directory or domain they are in.  This simple script also generates a CSV file with the results of password reset.

This script takes 2 arguments. First one is list(array) of user accounts and second argument is domain name to which these user accounts belongs to. The first argument is mandatory, and you should provide at least one user name. Otherwise script will not proceed. Second argument, domainname, is optional. By default, it assumes that all the user accounts you provided are present in the same domain as the account that is running the script.

Since this script doesn’t need much of explanation, we can jump straight into how to execute this script. Btw, this script relies on ActiveDirectory PowerShell module and uses Set-ADAccountPassword cmdlet to reset the password. If you don’t have this module, the script will fail to reset the password.

Script will prompt you to enter the password and confirm it again. It performs the comparison and ensures that you have entered it correctly both the times. It also checks that password is not less than 8 characters long. In both cases, it will exit with appropriate error message. So, make sure you are meeting required password complexity requirements. The checks here are simple ones and you can add more checks if you want.

Code:

Copy the below code into a file called Reset-UserPassword.ps1 and save it.

[CmdletBinding()]
param(
    [Parameter(Mandatory=$True)]
    [string[]]$UserName,
    [string]$DomainName = $env:USERDOMAIN
)

#Logic to read password

$password1 = Read-Host "Enter the password you want to set" -AsSecureString
$password2 = Read-Host "Confirm password" -AsSecureString
$pwd1_plain = (New-Object PSCredential "user",$password1).GetNetworkCredential().Password
$pwd2_plain = (New-Object PSCredential "user",$password2).GetNetworkCredential().Password

if($pwd1_plain -ne $pwd2_plain) {
    Write-Host "Either passwords didn't match" -ForegroundColor Yellow
    return
}

if($pwd1_plain.Length -lt 8) {
    Write-Host "Password length is less than 8 characters. Make sure you are meeting complexity requirements" -ForegroundColor Yellow
    return
}

$DEFAULT_PASSWORD = $password1

$OutCSV = @()
foreach($user in $UserName) {
    $OutputObj = New-Object -TypeName PSobject             
    $OutputObj | Add-Member -MemberType NoteProperty -Name DateTime -Value $(Get-Date)
    $OutputObj | Add-Member -MemberType NoteProperty -Name DomainName -Value $DomainName
    $OutputObj | Add-Member -MemberType NoteProperty -Name UserName -Value $User
    $OutputObj | Add-Member -MemberType NoteProperty -Name ResetBy -Value $env:USERNAME
    $OutputObj | Add-Member -MemberType NoteProperty -Name ResetStatus -Value $null
    

    Write-Verbose "$User : Working on retting the password"
    Write-Verbose "$User : Searching for the user in Active Directory"
    try {
        Set-ADAccountPassword -identity $User -Server $DomainName -Reset -NewPassword $DEFAULT_PASSWORD -EA Stop
        Write-Verbose "$User : Successfully Changed the password"
        Write-Host "$User : Password reset successfully" -ForegroundColor Green
        $OutputObj.ResetStatus = "SUCCESS"
    } catch {
        Write-Verbose "$user : Error occurred while resetting the password. $_"
        Write-Host "$User : Password reset FAILED" -ForegroundColor Yellow
        $OutputObj.ResetStatus = "FAILED : $_"

    }
    $OutCSV += $OutputObj
}

$outputfile = "c:\temp\PasswordResetStatus.csv"
Write-Host "Output is stored at $outputfile"
$OutCSV | export-csv -Path $outputfile  -NoTypeInformation -Force

Usage:

Now us see the ways you can use this script. Since all the user accounts we are located in techibee.local domain, I am passing it to the -DomainName parameter.

Reset a single user password

.\Reset-UserPassword.ps1 -UserName testuser1 -DomainName techibee.local

Reset multiple user’s passwords

.\Reset-UserPassword.ps1 -UserName testuser1,testuser2,testuser3 -DomainName techibee.local

Reset multiple user’s passwords by reading user lists from text file.

$users = Get-Content C:\temp\users.txt
.\Reset-UserPassword.ps1 -UserName $users -DomainName techibee.local

 

The output CSV generated by the script looks like below.

Hope this helps. Let us know if you have a requirement that you want to embed in the script. We will try accommodating it on best efforts basis. Please write in comments section.

{ 0 comments }

PowerShell: Disable IPv6 on network adapter in Windows

Today’s post is about disabling IPv6 protocol from given network adapter in Windows operating system using PowerShell. Reasons for disabling it will vary and we are not going to discuss about that topic now. We will purely focus on disabling it using PowerShell.

First task is to get list of network adapters in the computer. If you already know the name of the network adapter for which you want to disable IPv6, then you can proceed to next step directly.

Get-NetAdapter

This will show list of network adapters in the local computer. Let us say I want to check if IPv6 is enabled on network named LocalNetwork and then disable it.

We can first check if it is enabled on LocalNetwork by running below command.

Get-NetAdapterBinding -Name LocalNetwork

You can see in the output that Internet Protocol Version 6 (TCP/IPv6)(ms_tcpip6) is in enabled state. We can now disable it by running below command.

Disable-NetAdapterBinding -Name LocalNetwork -ComponentID ms_tcpip6 -PassThru

You can see the output that this component is disabled now. You can double check by below command again.

Get-NetAdapterBinding -Name LocalNetwork

Please note that above procedure will disable IPv6 on selected network adapter only. If you want to disable on total host, you need to follow separate steps.

 

Hope this helps.

{ 3 comments }

This post tasks about fetching the time zone information from remote(as well as local) computers using PowerShell. We will be using two approaches to fetch this information. First one is using WMI classes and other is using the PowerShell built-in cmdlet.

First we will see how to fetch this information using WMI classes since it works with all versions of Windows. Time zone information in windows can be accessed via Win32_TimeZone WMI class. Apart from time zone, this class provide various other information as well. You can read more about this class at MSDN(https://msdn.microsoft.com/en-us/library/aa394498(v=vs.85).aspx). I will limit this article to time zone details only. We will be fetching this information from Caption property.

Fetch local time zone using WMI query

Get-WMIObject -Class Win32_TimeZone

Fetch remote time zone using WMI Query

Get-WMIObject -Class Win32_TimeZone -Computer Server01

You can write a simple script around the below command to fetch this information for list of computers. The output of this script will be exported to CSV for ease of viewing the results.

Script:

[CmdletBinding()]
param(
    [string[]]$ComputerName =  $env:COMPUTERNAME
)
$Col = @()
foreach($Computer in $ComputerName) {
    if(!(Test-Connection -ComputerName $Computer -count 1 -Quiet)) {
        Write-Host "$Computer : Not Reachable" -ForegroundColor Yellow
        Continue
    }

    try {
        $tzinfo = Get-WMIObject -Class Win32_TimeZone -ComputerName $Computer -ErrorAction Stop
        $Col += $tzinfo | select-Object PSComputerName, Caption, StandardName, DaylightName
        Write-Host "$computer : Successfully fetched the time zone info" -ForegroundColor Green
    } catch {
        Write-Host "$Computer : Failed to fetch the Time zone info" -ForegroundColor Yellow
    }
}

$Col | export-csv -Path "c:\temp\timezoneinfo.csv" -NoTypeInformation


How to run the script?

#Read the servers list from text file to a variable
$servers = Get-Content c:\temp\servers.txt
#Run the script by passing servers list to it
.\Get-TimeZoneInfo.ps1 -ComputerName $servers

Output

Second approach is to use built-in cmdlets. PowerShell comes with two cmdlets Get-TimeZone & Set-TimeZone starting PowerShell v5.1. We will be using Get-TimeZone to see how can retrieve the information.

Running this cmdlet without any arguments gives the time zone information of local computer. If you need to run this to fetch remotely there is no -ComputerName parameter available. However, you can PowerShell Remoting to execute it on remote host. Make sure that remote system also must be running PowerShell v5.1 or higher. Otherwise this will not work.

Fetch local time zone using cmdlet

Get-TimeZone

Fetch Remote time zone using cmdlet

Invoke-Command -ComputerName SERVER01 -ScriptBlock { Get-TimeZone }

You can write a small script around the above command to run it against the multiple computers. The approach to do it is similar to what we have done for WMI based script above. You can give a try and post in comments section if you need help.

Hope this helps.

{ 1 comment }

PowerShell: Add Users to Group in Active Directory

Adding a User to Group in Active Directory is simple task and matter of one liner in most cases. However, building a script that can take multiple users as input and add them to a group is not equally simple. At the same time, it is not difficult as well. In this post, I will take you through a PowerShell script that adds given list of users to a group in Active Directory.

This script assumes that your user accounts and group are in same domain and you are running the script from a server/desktop which is also part of that domain. If you have a need for adding users from other domains to a group in different domain, drop a comment in this post, I will update the script to match your requirement on best effort basis.

Ok, let us go straight to the topic. We will leverage cmdlets from ActiveDirectory PowerShell module to achieve this.  Especially Add-ADGroupMember cmdlet to perform the addition of user to group.

Copy the below code into a file called Add-ADUserToGroup.ps1 file and save it.

Code

[CmdletBinding()] 
param( 
    [parameter(Mandatory=$true)] 
    [string[]]$UserName, 
    [parameter(Mandatory=$true)] 
    [string]$GroupName 
)

Import-Module ActiveDirectory
try {
    $GroupObj = Get-ADGroup -identity $GroupName -EA stop
} catch {
    Write-Warning "$GroupName : Group not found in Active Directory1" 
    return
}

foreach($userid in $UserName){ 
    try {
        $userobj = Get-ADUser -identity $userid -EA 0 
        if(!$userobj) { 
            Write-Warning "$userid : This account is not found" 
            continue 
        }
        Add-ADGroupMember -identity $GroupObj -Members $userobj -EA 0 
        Write-host "$userid : Successfully added to $GroupName" -ForegroundColor Green
    } catch { 
        Write-Warning "$userid : Failed to add to the group" 
    }
}

Usage instructions:

Using the script is easy. It takes 2 mandatory parameters.

  1. UserName : List of User names that you want to add it. This is same as user login ID(SamAccountName). You can provide single user name or multiple user names.
  2. GroupName: Name of the AD group to which you want to add the users.

Now let us see some usage scenarios. Throughout these examples we will use a group named “Sales Team Us” to perform the additions.

Add a single user to group:

Let us say you want to add user named labuser1000 into the Sales Team US group, use the below command.

Add-ADUserToGroup.ps1 -UserName labuser100 -GroupName "Sales Team US"

Add multiple users to group:

Let us say you want to add users names labuser100,labuser101 and labuser203 to Sales Team US group, then use the below command.

Add-ADUserToGroup.ps1 -UserName labuser100,labuser101,labuser203,nosuchuser -GroupName "Sales Team US"

Add users from text file to group:

In case you have a set of users which you have a text file(one user name per line) and you want to add them to Sales Team US group, then use the below commands. As you can see we are first reading the user names from text file into a variable called $users and then passing it to the script.

$users = Get-Content C:\users.txt
Add-ADUserToGroup.ps1 -UserName $users -GroupName "Sales Team US"

Any other use case you have in mind? Write in comments section.

{ 3 comments }

PowerShell: How to give Multi-line input to script

This article is about reading multi line input from command line using PowerShell. We often get requirement to enter multiple lines or paragraphs of text as input to a PowerShell script. The approach discussed in this article will help you with such requirements.

A few days back one of my friend asked me about this. He wants to write a script which takes a disclaimer as input, which is a multi-line. I told him that this is not a best approach. If you want to have such requirement, get the disclaimer in a text file and pass it to the script which is best approach in my opinion but his users are more comfortable with typing it at the console. Well, fine, requirements are always like this. So, I decided to help him with this script.

 

function Read-MultiLineInput {
[CmdletBinding()]
param(
)

$inputstring = @()
$lineinput = $null
while($lineinput -ne "qq") {
    $lineinput = Read-Host
    if($lineinput -eq "qq") {
        Continue
    } else {
        $inputstring += $lineinput

    }
}

return $inputstring

}


Write-Host "### Enter multi-line input ###"
Write-Host "(Type `"qq`" on a new line when finished)" -ForegroundColor Green
$multilines = Read-MultiLineInput
Write-Host "`n`n`n###Below is the multi line input you entered"
$multilines -join "`n"

 

Save the above code into a file called Read-MultiLineText.ps1 and execute it. As you can see on the console, it takes the input till you type “qq” on a new line. You can enter as many lines as you want. Just type qq on a new line when finished entering your multi line input.

This approach can be used if you want to enter multiple computer names or you want to copy paste computers list directly.

The logic in this script is simple. The Read-MultiLineInput function is repeatedly calling Read-Host till the value entered by the user is “qq”. When “qq” is found it is stopping the loop. I am sure this script will not cover all of the use cases. If you have a similar requirement and the script requires some enhancements, please post in the comments section. I will try comment/update on the best effort basis.

Below are some of the examples.

Example:1:

As you can see below, I have copy pasted first paragraph of this article as is and it accepted that.

Example 2:

I entered one computer name per line and as you can see it read all of them. Ideally you should declare a array of strings parameter and pass the computer names as camas separated. But most of the times our computer list is one computer name per line. To change it to one string with camas separated, we have to use a trick in excel or your favorite editor. We can avoid it using this approach.

Hope this helps.

{ 0 comments }

PowerShell: Search for a user without using AD Module

Active Directory Module is really useful to query Active Directory Domains and forests information. However, it is not possible to get this module installed everywhere because of various reasons. Sometimes it is do with the permissions required for installation and some times is the availability of RSAT binaries etc. So, in such cases, we can query active directory using Native abilities. Let us see how to do that.

To query Active Directory without using PowerShell module, we can use [ADSISearcher] accelerator. It does’t require any special binaries or components. It uses the underlying Directory Services .Net classes which are available by default in any windows system. In below example, I will show you how to search for a user account using CN attribute or SAMACCOUNTNAME.

First we need to prepare the LDAP queries.

Let us say we want to query all users whose CN starts with string “test”. The LDAP query is “(&(ObjectCategory=person)(ObjectClass=user)(cn=test*))”. We can pass this to [ADSISearcher] accelerator, as shown below.

Code:

$search = [adsisearcher]"(&(ObjectCategory=Person)(ObjectClass=User)(cn=test*))"
$users = $search.FindAll()
foreach($user in $users) {
    $CN = $user.Properties['CN']
    $DisplayName = $user.Properties['DisplayName']
    $SamAccountName = $user.Properties['SamAccountName']
    "CN is $CN"
    "Display Name is $DisplayName"
    "SamAccountName is $SamAccountName"
}

Similarly you can find for a specific user by his login name using the LDAP query “(&(ObjectCategory=person)(ObjectClass=user)(samaccountname=testuser1))”. You can update this filter in above code and run it again to get the testuser1 details.

This is just a sample. You can do many more such things with this approach.

{ 0 comments }

There are various methods available to generate list of Domains and Domain Controllers in current forest or a given forest. In this post we will explore some of these options and see how to generate this list using PowerShell.

We can get the list of Domains and Domain Controllers using two possible ways.

  1. Active Directory PowerShell Module
  2. .Net Classes

Active Directory PowerShell Module

The first method is very simple to use. You just need Active Directory PowerShell module installed on a computer so that you can make use of Get-ADForest & Get-ADDomainController cmdlets to query this information. I have written a small function which can get this information for any forest as long as you have trust with computer from where you are running this code.

Copy below Get-DCsInForest PowerShell function into your PowerShell window and call it by passing any of the Domain name in the forest for which you want to generate the inventory. Incase you want to query the forest to which logged on user belongs to, just don’t pass any parameters.

Code

function Get-DCsInForest {
[CmdletBinding()]
param(
    [string]$ReferenceDomain = $env:USERDOMAIN
)

$ForestObj = Get-ADForest -Server $ReferenceDomain
foreach($Domain in $ForestObj.Domains) {
    Get-ADDomainController -Filter * -Server $Domain | select Domain,HostName,Site
    
}

}

Examples:

Query all Domain Controllers in current forest.


Get-DCsInForest

Query all Domain Controllers in other forest.


Get-DCsInForest -ReferenceDomain techibee.local

.Net Classes

It is possible that you may not have Active Directory module in all boxes. So, is it must to have this module to generate the inventory? Absolutely not. There are other ways available for this. You can use System.DirectoryServices.ActiveDirectory name space and the classes init to generate the inventory.

You can use below one-liner to generate the inventory.

Code

[System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().Sites | % { $_.Servers } | select Domain,Name,SiteName

You can also export it to CSV using below command.

 \
$DCs = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().Sites | % { $_.Servers } | select Domain,Name,SiteName 
$DCs | export-csv c:\DCsInventory.csv -NotypeInformation

 

Hope this is helpful

{ 0 comments }

In this post, we will discuss how to set or remove the Password Never Expires check box in Active Directory User object properties under the Account tab. Using this script mentioned in this post, you can do it for single or multiple users accounts.

This script relies on Get-ADUser and Set-ADUser cmdlets in ActiveDirectory module. So make sure it is installed before you run this script. Script has two inputs. First one is list of user accounts for which you want to set or remove the password never expires option. The user accounts list can be from a text file with one user account per line or can be passed directly to the parameter as a comma separated values. Second input is what is operation you want to perform, i.e set or remove operation.

Input parameters:

  1. UserAccounts : List of user accounts which you want to set or remove the password never expires operation
  2. SetOption : Enables Password Never Expires option if not already enabled.
  3. RemoveOption : Removes Password Never expires option if enabled

You can look at the example section below to understand how to use this script. The output of the script will clearly indicate the status for each account whether it has enabled it or there are some errors etc.

 

Script : Update-PasswordNeverExpires.ps1


[CmdletBinding()]
param(
    [Parameter(Mandatory=$true)]
    [string[]]$UserAccounts,
    [Parameter(ParameterSetName="set", Mandatory=$true)]
    [switch]$SetOption,
    [Parameter(ParameterSetName="remove", Mandatory=$true)]
    [switch]$RemoveOption
)

foreach($UserAccount in $UserAccounts) {
    try {
        $UserObj = Get-ADUser -Identity $UserAccount -EA Stop -Properties PasswordNeverExpires
        if($UserObj.PasswordNeverExpires) {
            if($RemoveOption) {
                Set-ADUser -Identity $UserAccount -PasswordNeverExpires:$false -EA Stop
                Write-Host "$UserAccount : Successfully removed the password never expires option" -ForegroundColor Green
            } else {
                Write-Host "$UserAccount : Option already enabled" -ForegroundColor Yellow
            }
        } else {
           if($SetOption) {
               Set-ADUser -Identity $UserAccount -PasswordNeverExpires:$true -EA Stop
               Write-Host "$UserAccount : Successfully enabled password never expires option" -ForegroundColor Green
           } else {
               Write-host "$UserAccount : Option already removed" -ForegroundColor Yellow
           }

       }

    } catch {
      Write-host "$UserAccount : Error Occurred. $_" -ForegroundColor Red

}

}

Examples:

Set Password Never expires option for Single User


Update-PasswordNeverExpiresFlag.ps1 -UserAccounts LabUser01 -SetOption

Remove Password Never expires option for Single User


Update-PasswordNeverExpiresFlag.ps1 -UserAccounts LabUser01 -RemoveOption

Set Password Never Expires option for multiple users


$Users = Get-Content c:\temp\users.txt

Update-PasswordNeverExpiresFlag.ps1 -UserAccounts $Users -SetOption

Remove Password Never Expires option for multiple users


$Users = Get-Content c:\temp\users.txt

Update-PasswordNeverExpiresFlag.ps1 -UserAccounts $Users -RemoveOption

As you can see from the screenshots, the output of the script will give you the status of execution.

Hope this helps.

 

{ 7 comments }

Get closest domain controller using PowerShell

Finding nearest domain controller for a given Active Directory domain is very useful when writing scripts using ActiveDirectory PowerShell module in multi-domain/forest environments.

This is because the cmdlets in ActiveDirectory module will by default query the domains controllers that belongs to local machine domain. If you need to query any other domains, then you need to pass the value of domain controller from that domain to -Server parameter of the cmdlet. Some people will use FQDN of the domain and pass it to -Server parameter to query that domain. While it works, it may cause slowness sometimes as you don’t know which DC you are connecting to perform the search or update operation. So, the better way is to query the nearest domain controller in that domain and use it to perform the operation. This way it is easy to debug any search slowness or update issues.

Ok, let us now proceed. You can find nearest domain controller of a domain using Get-ADDomainController cmdlet. This cmdlet has variety of options. We will be focusing on couple of them to get the results we need.

Query the nearest domain controller of current domain

Below code will print the FQDN of the domain which is in your local site. If there is no DC in your local AD site, then it will return one from nearest AD site.


$DC = Get-ADDomainController -Discover

$DCName = $DC.Hostname

write-host $DCName

Query nearest domain controller for other domain


$DC = Get-ADDomainController -Discover -DomainName techibee.local

$DCName = $DC.Hostname

Write-host $DCName

Query the Domain Controller that holds PDC role


$DC = Get-ADDomainController -Discover -Service PrimaryDC

$DCName = $DC.Hostname

Write-host $DCName

Query a writable domain controller if you have RODCs in your domains


$DC = Get-ADDomainController -Discover -Writable

$DCName = $DC.Hostname

Write-host $DCName

Query a domain controller which has at least Windows Server 2008 operating system


$DC = Get-ADDomainController -Discover -MinimumDirectoryServiceVersion Windows2008

$DCName = $DC.Hostname

Write-host $DCName

Once you have the DC details, you can pass it to other cmdlets to query information from it. For example, you can pass it to Get-ADUser cmdlet to query all users whose name starts with labuser.


Get-ADUser -Filter { name -like "lab*"} -Server $DC

Hope this helps. Let me know if you have any other scenario that you want to query DCs.

{ 0 comments }

Convert a string into number using PowerShell

In this post, I will show you how to convert a number in string format to integer. Before we jump further and see how to do it, I want to begin with help you understand why we need to do it. If a number is stored as a string, you can cannot perform addition or any other numeric operations on that variable until you convert it to integer or any other numeric types.

Let us see an example of what I am mean. As you can see from the below screenshot, a number(100) is stored in $string variable. When I perform an addition operation on it expecting the value to increase to 110, but it appended 10 to 100 in string fashion and returning the value of 10010. So, it is necessary to convert to a numeric value before we perform addition.


$string="100"

$string + 10

Converting a string to number is very easy. For this purpose I am using Parse() method in System.Int32 class.


$string = "100"

$numval = [int]::Parse($string)

$numval

$numval.gettype()

$numval + 10

As you can see from the output, the $numval variable is a int32 type and now the addition operation on the value is working as expected.

Hope this helps.

{ 0 comments }