≡ Menu

PowerShell: Creating self signed certificate

Let us learn today how to generate a self signed certificate quickly using PowerShell. There are many ways available to do this but using New-SelfSignedCertificate is much easier.

You need to have at least Windows Server 2012/Windows 8 to use this approach. Once the cert is generated you can use it anywhere you want.

First you need to run below command to generate the certificate. Subject name can be anything of your choice. You can use DnsName parameter if you want.


$cert = New-SelfSignedCertificate -Subject "techibee-test-cert"

It creates the cert by default in LocalMachine\MY cert store. If you wish to create in any other store, you can do so.

Now you can export it to a file(PFX format) using Export-PFXCertificate cmdlet. To export the cert, password is mandatory.

$password = ConvertTo-SecureString "mypassword" -AsPlainText -Force

Export-PfxCertificate -Cert $cert -FilePath c:\mycert.pfx -Password $password

The above command will export the cert in PFX format with password protection and it will be placed in c:\ drive with mycert.pfx name. Change FilePath values if you want to store at different location.

Now you can use this cert for your testing etc.

Below is the screenshot of the execution.

Hope it helps.

{ 3 comments }

PowerShell: Verify or test AD account credentials

We prompt for credentials in many of our scripts. Have you ever wondered if the entered credentials is right or wrong? It is always good idea to verify that entered credentials is correct before proceeding with further execution of the script.

The function discussed in this article will provide a easy way to verify if the entered credentials are valid or not. This function uses classes and methods in System.DirectoryServices.AccountManagement name space, especially ValidateCredentials method.

The Test-ADCredential function takes PSCredential argument as input. This is optional one. If you don’t specify it, a prompt will appear for you enter the credentials. That means we can use this function in our scripts as well as for adhoc testing needs.

Code

function Test-ADCrential{
    [CmdletBinding()]
    param(
        [pscredential]$Credential
    )
    
    try {
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement
        if(!$Credential) {
            $Credential = Get-Credential -EA Stop
        }
        if($Credential.username.split("\").count -ne 2) {
            throw "You haven't entered credentials in DOMAIN\USERNAME format. Given value : $($Credential.Username)"
        }
    
        $DomainName = $Credential.username.Split("\")[0]
        $UserName = $Credential.username.Split("\")[1]
        $Password = $Credential.GetNetworkCredential().Password
    
        $PC = New-Object System.DirectoryServices.AccountManagement.PrincipalContext([System.DirectoryServices.AccountManagement.ContextType]::Domain, $DomainName)
        if($PC.ValidateCredentials($UserName,$Password)) {
            Write-Verbose "Credential validation successful for $($Credential.Username)"
            return $True
        } else {
            throw "Credential validation failed for $($Credential.Username)"
        }
    } catch {
        Write-Verbose "Error occurred while performing credential validation. $_"
        return $False
    }
}

Output

Run the function without arguments and it will prompt you to enter credentials

Test-ADCredential

Run the function by passing credential object as argument and it will return the output straightway.

$myCreds = Get-Credential
Test-ADCredential -Credential $Mycreds

Do you have any questions about how this function works? Please write in the comments section, we will get back on that.

{ 3 comments }

Python: Get current date and time

Today’s post is about finding today’s date and time using Python. This script queries local system for today’s date and time in local time zone.

We will be using python built-in module called datetime for this purpose. This module has a function called now() which will return a date time object. Let see how it looks like.

import datetime as dt
dt.datetime.now()

If you execute above lines of code from Python REPL, you will see that it returns a date time object as shown below.

As you can see in the output it returned datetime in object format. It is very convenient to use this object if you are going to consume this further in your scripts. However, at times we want to print the time that humans can easily understand. For example, you want to print date time in MM-DD-YYYY format. To do that we can use strftime() method which is available on the datetime object. Let us see how to use it.

import datetime as dt
date = dt.datetime.now()
print("Today's date time : {0}".format(date.strftime("%d-%m-%Y %H-%M-%S")))
print("Today's date time : {0}".format(date.strftime("%D %T")))

Execute the above code form repl and you will see the below output.
We are using strftime() method to format the date time the way we want. If you want to explore other formats available with strftime(), refer the below table(source: python.org)

 

Directive Meaning Notes
%a Locale’s abbreviated weekday name.
%A Locale’s full weekday name.
%b Locale’s abbreviated month name.
%B Locale’s full month name.
%c Locale’s appropriate date and time representation.
%d Day of the month as a decimal number [01,31].
%H Hour (24-hour clock) as a decimal number [00,23].
%I Hour (12-hour clock) as a decimal number [01,12].
%j Day of the year as a decimal number [001,366].
%m Month as a decimal number [01,12].
%M Minute as a decimal number [00,59].
%p Locale’s equivalent of either AM or PM. (1)
%S Second as a decimal number [00,61]. (2)
%U Week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Sunday are considered to be in week 0. (3)
%w Weekday as a decimal number [0(Sunday),6].
%W Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Monday are considered to be in week 0. (3)
%x Locale’s appropriate date representation.
%X Locale’s appropriate time representation.
%y Year without century as a decimal number [00,99].
%Y Year with century as a decimal number.
%Z Time zone name (no characters if no time zone exists).
%% A literal '%' character.

 

Notes:

  1. When used with the strptime() function, the %p directive only affects the output hour field if the %I directive is used to parse the hour.
  2. The range really is 0 to 61; this accounts for leap seconds and the (very rare) double leap seconds.
  3. When used with the strptime() function, %U and %W are only used in calculations when the day of the week and the year are specified.

Hope this article is helpful.

{ 0 comments }

PowerShell: Get Domain NetBIOS name from FQDN

How to find NetBIOS name of a Active Directory domain if you know the FQDN is something we will explore in this post. There are multiple ways to achieve this and let us see two of the popular and easy ways to do it.

We can get this information using two ways.

  1. Using Active Directory PowerShell cmdlet Get-ADDomain
  2. Using [ADSI] Powershell accelerator

Using Get-ADDomain.

To use this cmdlet you should have ActiveDirectory module installed as Get-ADDomain is part of this module. Once the module is imported with Import-Module cmdlet, you can get the NetBIOS domain name by passing FQDN as shown below.

Import-Module ActiveDirectory
(Get-ADDomain -Server techibee.local).NetBIOSName

Using [ADSI] PowerShell accelerator

Getting the NetBIOS name using Get-ADDomain cmdlet is very straightforwad and easy, but you cannot have this module installed on all the machines you want to run your script from. For example, you are scheduling a script on all computers in your environment in which you want to get the NetBIOS name, then it is practically impossible to deploy/maintain the module on all computers. Also this module requires Active Directory Web Services to be available which is available from Windows 2008 R2 onwards. If your domain has legacy DCs, Get-ADDomain will not work for you. The below sample code will help you such situations. It don’t have any dependency on the external modules, it relies on .Net classes to get the NetBIOS name from domain FQDN

([ADSI]"LDAP://techibee.local").dc

Do you know any other easy way? Please write in the comments section. It will be added to this article.

 

{ 4 comments }

[SOLVED] Wi-Fi on Windows 10 is slow

You may experience slow internet connection on your Windows 10 PC when you have more than one active network connection. This article helps you to resolve such issues and make the internet browsing experience faster.

You might wondering why a computer need multiple network connection, but yes, it is a possible scenario. Some people use Wi-Fi connectivity for browsing and LAN connection for connecting to work place network etc. So, it is quite a possible scenario.

The issue will surface when one of the connections has internet access and other connection doesn’t have. You will start noticing internet connection taking more time and your browser spends time at Resolving host state(you can notice this chrome) during this process. If you ask any Windows expert, they will say change the network order to ensure adapter with network connection is first in the list.

In older version of windows, there is a concept of network adapter order. That means OS will chose first one in the list to perform network related operations and if it fails goes to the next one. See below image for better understanding.

Image source: Microsoft Networking Blog

But this is improved in Windows 10 and now there no concept of ordering the network as OS is capable of taking the intelligent decisions based on a few factors. However when it comes to DNS name resolution, the components still looks for ordering (source: https://blogs.technet.microsoft.com/networking/2015/08/14/adjusting-the-network-protocol-bindings-in-windows-10/) and they use InterfaceMetric property of the adapter to make the choice. The lower this number, the higher the preference for dns name resolution.

So, this is problem we are hit with. Generally Wi-Fi networks InterfaceMetric value is more than LAN adapter value. So, the name resolution process will try to use the DNS servers configured in LAN adapter first to resolve the internet names like google.com etc. When it fails, then it will goto the next adapter i. e Wi-Fi adapter. The problem is worse when you have more than 2 adapters (possible when you install Hyper-V role on Windows 10).

I tried following the suggestion at Microsoft blog(https://blogs.technet.microsoft.com/networking/2015/08/14/adjusting-the-network-protocol-bindings-in-windows-10/) but it didn’t work for me. I believe the logic in that blog to determine the existing order is incorrect. So, I did a bit of experimenting and came up with below approach which worked great for me. In my case, I have more than 2 adapters as I am running Hyper-V role.

First we need to check what is the existing metric values for the adapters. We can do it by running below command from a PowerShell window with Administrator rights. Make a note of the current values so that you can revert to original settings if the fix is not working for you.

Get-NetIPInterface -AddressFamily IPv4 | select ifIndex,ifAlias,interfaceMetric

As you can see, my Wi-fi adapter metric is 50 and there are other adapters that value less than 50. That means Operating System will first try to resolve the name using the DNS servers configured on those adapters and it comes to Wi-Fi if it gets no response. Since they are private networks, resolving internet names will not work through them. Now we need to change the metric to a value lower than these adapters. So, I decided to set the metric value of Wi-Fi adapter to 10 so that it becomes less than other adapters. I have used below command to do this.

Set-NetIPInterface -InterfaceAlias "Wi-fi" -InterfaceMetric 10

Once this is executed without issues, you can restart the box and you will notice internet experience is back to normal. If not, revert back to old value to running the same command with original InterfaceMetric Value.

Please note that this is personal experience and may or may not work in your case. So, understand the scenario of your problem and decide if this fix will help you. Please write in comments section if this fix helped you or not. If you know any other fix, please share it.

 

 

 

{ 2 comments }

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.

{ 2 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.

{ 8 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.

{ 3 comments }

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.trim() -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.

{ 7 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 }