PowerShell: List all DL Members recursively

I had a situation to list all members of distribution group (DL) or security group to validate the members. Problem was the DL had other DLs as members. I had to write a quick script to list all members recursively.

If you have the same requirement of listing all members of a DL, feel free to use my script. It create a CSV file of the member list.

Make sure you connect to you Exchange environment first in PowerShell. It works Exchange Online and On-Prem Exchange servers.

    This script lists all members of a distribution list including all 
    child groups members recursively. It create a CSV file with all
    groups of the given distribution group.

    Parameter: Distribution group Alias or Name or Email Address

    .\List-DLMembers.ps1 -DLName "NA-Sales"


Function    : Expand-Group
Parameter   : Distribution Group Name
Description : This function populates all the members of the
given distribution group to a global variable named $Global:Users 

Function Expand-Group ($GroupName)
    $members = Get-DistributionGroupMember -Identity $GroupName

    foreach($member in $members)
        $RecipientType = (Get-Recipient $member.Alias).RecipientType
        $member.Name + "`t`t`t" + $RecipientType
        if ($RecipientType -like "*DistributionGroup*")
            # Found an child group - calling myself to expand the group
            Expand-Group -GroupName $member.Alias

        # Create a PSCustomObject of the current member
        $MemberObject = [PSCustomObject] @{
            Name = "$($member.Name)" 
            Title = "$($member.Title)" 
            Department = "$($member.Department)" 
            Email = "$($member.PrimarySMTPAddress)" 
            Memberof = "$GroupName"

        # Store the member object to Users array
        $global:users += $MemberObject
    End of the Function

 °º¤ø,¸¸,ø¤º°`°º¤ø,¸,ø¤°º¤ø,¸¸,ø¤º°`°º¤ø,¸ °º¤ø,¸¸,ø¤º°`°º¤ø,¸,ø¤°º¤ø,¸¸,ø¤º°`°º¤ø,¸

                THE SCRIPT STARTS HERE

 °º¤ø,¸¸,ø¤º°`°º¤ø,¸,ø¤°º¤ø,¸¸,ø¤º°`°º¤ø,¸ °º¤ø,¸¸,ø¤º°`°º¤ø,¸,ø¤°º¤ø,¸¸,ø¤º°`°º¤ø,¸

# Create a Global Array Variable to store all DL member objects
$global:users = @()

# Call the Expand-Group function to populate the all DL members
Expand-Group -GroupName $DLname

#Store the member objects to a CSV file
$filename = ".\$DLName-Members.csv"
$global:users | ConvertTo-Csv | Out-File -FilePath $filename

 °º¤ø,¸¸,ø¤º°`°º¤ø,¸,ø¤°º¤ø,¸¸,ø¤º°`°º¤ø,¸ °º¤ø,¸¸,ø¤º°`°º¤ø,¸,ø¤°º¤ø,¸¸,ø¤º°`°º¤ø,¸

                END OF THE SCRIPT

 °º¤ø,¸¸,ø¤º°`°º¤ø,¸,ø¤°º¤ø,¸¸,ø¤º°`°º¤ø,¸ °º¤ø,¸¸,ø¤º°`°º¤ø,¸,ø¤°º¤ø,¸¸,ø¤º°`°º¤ø,¸


PowerShell Tip: How to remove items from an Array? Yes, it is possible.

Are you trying to figure out how to remove array items from an Array? Were you end up coding a foreach loop & create a new array?

I have an easy way without using new array or foreach loop. Here it is:

$ArrayVar = $ArrayVar | Where-Object{-not($_ -eq 'ItemValue')}

Now to describe how it been used in a code…I created a quick sample code. Note that I was removing two items at once at line number 9.

$SuperHeros = @("Thor","IronMan","AntMan","Hulk","Clint Barton","DeadPool","Captain Marvel","Black Widow")
"Here are the Super Heros:"

"`n`nOops..Black Widow and Clint Barton are not really super heros..Remove them at once."
"Yes Sir."

#Remove Black Widow and Clint Barton...Are they Superheros? or side kicks?
$SuperHeros = $SuperHeros | Where-Object {-not(($_ -eq 'Black Widow') -or ($_ -eq 'Clint Barton'))}

"`n`nNew List of SuperHeros:"

I ran this code and here is the result:

Here are the Super Heros:
Clint Barton
Captain Marvel
Black Widow

Oops..Black Widow and Clint Barton are not really super heros..Remove them at once.
Yes Sir.

New List of SuperHeros:
Captain Marvel

Hope you like this trick. 😉 Leave me a reply!

PowerShell: Combine CSV files into a single CSV file

I had a need to combine multiple CSV files. And worked for few minutes and came up with this PowerShell method.

Get-ChildItem -Filter *.csv -Path C:\Scripts\CSVFiles | Select-Object -ExpandProperty FullName | Import-Csv | Export-Csv C:\Scripts\CSVFiles\CombinedFile.csv -NoTypeInformation -Append

Idea of this method is

  1. Get the filenames with full path of the CSV files
  2. Import them all
  3. Pipe the import the CSV files to Export-CSV to a file. Note the “-Append” parameter.

Hope this help you too.

CSV Viewer – an awesome Powershell solution

When working with some CSV files in my Windows Servers, I was wondering how can I view the CSV file without installing any third-party viewers.

Here is my one-liner PowerShell solution. I am using Out-GridView with “-Wait” option. Make sure PowerShell Execution Policy is set to run local scripts.

Here is how you do it

Log onto to your Windows Server (or on your local computer).
Open Notepad.

Copy and Paste this code into Notepad:

REM ...:::>  Anand, the Awesome  <:::...
Start /Wait Powershell -Command "Import-Csv -Path %1 | Out-GridView -Title %1 -Wait"

Save this file as CSV-Viewer.bat in your favorite location.

Now find a CSV file on your disk, right-click and go to Properties. Change Open With to the CSV-Viewer.bat file that you created in above step.

That’s all. Now try double-clicking on any CSV file. It will open GridView. You can search things in the CSV file, or sort any column or Filter the data.

Exchange: Calendar Folder Permissions and Delegates

There are two kinds of permission for Calendar in Mailbox. A user can have

  • Folder permission to calendar
  • Delegate permissions

Folder permission is used by another user to access the calendar and Delegate permission is given to a user to manage the calendar behalf of the giving user. Both kinds of permission can be given to user from Outlook or by Exchange PowerShell.

How to do it in Outlook?

Folder permission: In Outlook Go to Calendar section, right click on the calendar on the left pane, Choose properties/Permissions.

Delegate permission: In Outlook, click File / Account Settings / Delegate Access.

Detailed Help: https://support.office.com/en-us/article/Allow-someone-else-to-manage-your-mail-and-calendar-41C40C04-3BD1-4D22-963A-28EAFEC25926

How to do it in Exchange Admin Shell?

Open PowerShell and connect to Exchange (or Exchange Online).

Folder Permission:

Give John.Doe@company.com Reviewer access to Jane.Doe@company.com’s calendar.

Add-MailboxFolderPermission -Identity Jane.Doe@company.com:\Calendar -User john.doe@company.com -AccessRights Reviwer

Delegate Access:

Make John.Doe@company.com as Delegate with Editor access to Jane.Doe@company.com’s calendar.

Add-MailboxFolderPermission -Identity Jane.Doe@company.com:\Calendar -User john.doe@company.com -AccessRights Editor -SharingPermissionFlags Delegate 

Detailed Help: https://docs.microsoft.com/en-us/powershell/module/exchange/mailboxes/add-mailboxfolderpermission?view=exchange-ps

Exchange: Mailbox Folder Size Report

As an Exchange Administrator, you can generate a mailbox folder size report for any user. It is extremely helpful if the USER doesn’t know how his/her mailbox is reaching size limit & which FOLDER has most emails.

Well here is the one-liner PowerShell script (Note: Replace username@company.com with user’s email address):

Get-MailboxFolderStatistics username@company.com | Select-Object FolderPath,@{Name="SubFoldersize";Expression={$r=$_.FolderAndSubfolderSize; [int] $a = ($r.Substring($r.IndexOf("(")+1,($r.Length - 2 - $r.IndexOf("("))) -replace " bytes","" -replace ",","") ; $a } } | Sort-Object SubFolderSize -Descending | Select-Object FolderPath,@{Name="Folder-and-Subfolder-Size";Expression={[String] $_.SubFolderSize + " MB"}}

The sample output shows this:

FolderPath                                       Folder-and-Subfolder-Size
 ----------                                       -------------------------
 /Scheduled Reports                               1625 MB
 /incoming Unapproved emails                      1165 MB
 /Audits                                          907 MB
 /Compliants                                      562 MB
 /Policy Violation Compliants                     551 MB
 /Deleted Items                                   482 MB
 /Server Activity Summary                         449 MB
 /VM Approval Requests                            236 MB
 /Approved Requests                               71 MB
 /New Accounts                                    61 MB
 /Profile Review                                  57 MB
 /Authorizations                                  35 MB
 /Rejected Content                                11 MB
 /Sent Items                                      8 MB
 /PersonMetadata                                  6 MB
 /Contacts                                        3 MB
 /Contacts/Recipient Cache                        3 MB
 /Calendar                                        0 MB
 /Drafts                                          0 MB
 /Quick Step Settings                             0 MB

You can export this to CSV file by adding “| Export-CSV -Path .\MBFolderSizeReport.csv -NoTypeInformation”. If it helped you, leave me a “Thanks” in the comments.

Windows Server 2016/2019: Where the hell the time zone settings?

If you are trying right-clicking on Date/Time on task bar, choosing Adjust date/time:

Configure Date, Time and Time Zone settings in Windows Server 2016

And you are stuck at Windows new settings page, where changing the time zone doesn’t take effect. Here you say: “What the HELL?”

So, I say go for old Time settings from control panel to change the time zone. Here is how you can do it:

  • Press Windows Key + R to open the Run dialog.
  • Type “control timedate.cpl

You have been served with the old setting dialog, change the time zone here by clicking on “Change Time Zone” button

Or you can also get here from the Server Manager >> Local Server >> Click on the Timezone..

Hope this blog is useful for you.

Promoting a DC: Error determining whether the target environment require adprep

When you are promoting a new Domain Controller in to an existing Active Directory Forest/Domain, you let the default setting of “Any domain controller” to sync from and you are stuck at promotion process with this error message:

Error determining whether the target environment require adprep:  
Validation error Validation error: Unable to make an LDAP connection to server RandomDC01.company.com 
Exception: The specified server cannot perform the requested operation  Details: Test.VerifyForestUpgradeStatus.ADPrep.Win32Exception.-2147467259

If I choose a closest domain controller manually, you still the ‘freaking’ error message:

Error determining whether the target environment require adprep:
Validation error Validation error: Unable to check forest upgrade status for server ClosestDC1.company.com
Exception: The specified server cannot perform the requested operation
Details: Test.VerifyForestUpgradeStatus.ADPrep.Win32Exception.-2147467259

OK, now. What seems to be the ‘problem’? I believe the new server being promoted to a DC is NOT seeing the network or any domain controllers correctly. Check these few possible issues/solutions:

  1. Is the new server pings any other domain controllers? Can you Telnet port 389? Fix the Network card (IP, subnet or bad driver)
  2. Is any Firewall between the new server to existing domain controllers? Check here what firewall ports needs to open – https://support.microsoft.com/en-us/help/179442/how-to-configure-a-firewall-for-domains-and-trusts
  3. Is this new server a VM? check it is connected to correct (software) swtich/VLAN.

When you demoting a domain controller, you receive the “Failed to modify the necessary properties for the machine account. Access is denied” error message

Are you getting this error message when demoting a domain controller?

“The operation failed because: The Active Directory Domain Services Installation Wizard (DCpromo.exe) would not configure the computer account <2012 DC> on the remote Active Directory Domain Controller <2019 DC>. Verify that the user running dcpromo.exe is granted the “Enable computer and user accounts to be trusted for delegation” user right in the Default Domain Controllers Policy. The error was: Access is denied”

If you didn’t enable the GPO setting for “Enable computer and user accounts to be trusted for delegation”, by all means enable it and then run GPUpdate /force command on the domain controller before demoting the DC.

If the GPO setting is already enabled, AD replication is done, GPUpdate.exe updated the setting on the domain controllers and you see the setting enabled in RSOP.exe results. BUT you still getting the same freaking error again. What do you do now?

In my case, computer account for Domain Controller is enabled with the setting “Protect object from accidental deletion“. I disabled this check box, then demotion went without errors.

See the source image

Active Directory: Self-Signed certificate for LDAPs

While testing Active Directory on a closed private network, I needed LDAPs connections to the domain controllers. But I didn’t have any PKI/Certificate servers on the network and I didn’t want to build one.

So I decided to use a self-signed SSL certificate for LDAPs connections. If you reading this, you need one too. Here is how I did it.

  • Logon to the Domain Controller
  • Open PowerShell in elevated mode (Right-click on PowerShell, choose Run as Administrator)
  • Next Run the New-SelfSignedCetificate cmdlet. NOTE: Replace the DnsName to your server’s FQDN name and NetBIOS name.
New-SelfSignedCertificate -DnsName SanFran-DC01.company.com, sanfran-dc01 -CertStoreLocation cert:\LocalMachine\My 
  • This cmdlet will create a self-signed cert with given DNSName and place it at Local Computer certificate store.
  • Last step: Open Certificate console by click Start button & type Manage Computer Certificates and open it from the search results.
  • Expand Personal >> Certificates and also expand Trusted Root Certificates >> Certificates
  • Find the newly generated Self-Signed SSL Certificate in Personal >> Certificates.
  • Select the Self-Signed Certificate and drag & drop to Trusted Room Certificates >> Certificates to trust the certificate on the domain controller.
  • Close the Certificate console

Now you are ready to do LDAPs to this domain controller. If you want to validate it works, you can use LDP.exe tool.

  • Open LDP.exe on the domain controller (or any other computer on the network)
  • Click Connection menu and choose Connect…
  • Type the domain controller FDQN and Port number as 636 and click OK. You should see “Established connection to <domain controller>” and the Base DN details.

Did this blog help you? Leave me a reply!