Recently I was taking an inventory of Distribution Lists in my messaging infrastructure, which lead me to take an additional step to look at the statistics about the total number of members and the usage of each group. As you would be wondering, it wouldn’t be as easy as we think if we plan to achieve it without a script. Here is one simple script, which let you extract the DL usage statistics with the following information,

-          The total number of users in each Distribution Group

-          The total number of times the group has used in the past number of days

Most Exchange Server Administrators keep the tracking log retention period as 30 or 60 days. Which means you will only be able to extract usage statistics for the past number of days equal to the message tracking log retention.

Permissions Required: You need to login as Server Admins to get the result or a user who has access to message tracking and Get-TransportServer cmdlet, as the script looks for all transport servers.

Download Script
How to Use?

You can run the script with or without specific parameters,

Examples,

.\DLStatistics.ps1 [Get you ALL the DL Information with members and usage statistics for last 7 days]

.\DLStatistics.ps1 -Days [Usage Days] -filePath [Give the report Path]

    e.g. {.\DL_Usage_Stats.ps1 -Days 1 -filePath C:\scripts\Dev\}

DL Usage Examples1

    Default File Path is C:\Scripts and the Default usage days is 7 days 

Though the script can be run with n number of days duration, I suggest you to run it for a week to get a report faster. The following information will give you a fare idea about the total time the script takes to complete,

Total Number of Groups = ~100

Total Tracking Log Size (all servers) = 8GB

Total time taken = 1- 2 hours. 

Once you test the script succesfully, I suggest you to write a batch file and schedule it to run every week so that you can create statitical information for longer periods.

Download Script

The script can be easily modified to include general DL property field as per your requirements, add additional properties just after the following section similar to below,

$reportObj | Add-Member NoteProperty -Name "Group Name" -Value $DL.DisplayName
$reportObj | Add-Member NoteProperty -Name "Email Address" -Value $DL.PrimarySMTPAddress 

Ensure you keep the format similar to above, which should avoid any confusion at the end. It is important for you to know the Distribution Group property field name such as alias,ManagedBy etc. Get-DistributionGroup command can help you get all field names.

Share your comments to improve the script to match the regular requirements.

Download Script

-Praveen

Published in Solutions

We all know that the OWA page (ECP in purticular) has the option to update the user contact information. However, at time you might in need for a script to enable the similar feature, such as when the OWA feature is not available to all user. I had similar situation in my company, where the OWA feature is not enabled for ALL users, and forced to develop a small PS script which enables the users to update their on contact information self.

Download the Script Here

Following script gives you a sample, and can extend the fields according to your requirement. Below shows the interface that each user gets, and the username field is choosen authomatically from the user's login page. That makes it clear that the script can only run by the users who directly logged into domain joined computers with their network credentials.

 GAL Script

Download the Script Here

As you are aware, the unsigned script can not be executed from the user machine, unless you change the restriction setting of windows powershell. The following section discuss about how to by pass the powershell execution policy.

How to by pass Powershell Restriction

You may run the .ps1 script with the switch "Bypass" when running the command.

e.g. PowerShell.exe -NoProfile -nologo -ExecutionPolicy Bypass -File ./script.ps1

Or you may create a batch file (.bat) and call the ps1 script from it, sample batch file entry will look like this,

@ECHO OFF
PowerShell.exe -NoProfile -nologo -ExecutionPolicy Bypass -Command "& '\\server\GAL\UpdatePhone_v2.PS1'"

Save the above line in a .bat file and run it from any computer.

 

Download the Script Here

Share you comments.

-Praveen

 

Published in Articles

We, Microsoft Exchange Admins, usually or frequently edit hosts file to verify some changes prior to the production. The below script will enable you to modify (adding or removing host entries) the hosts file from a remote computer. The script will also record the machine names against the host entries are changed.

There are two scripts, first one to add the entries to the hosts file, and the second one for removing it once any testing is completed.

Script for Adding Entries to Hosts file

function add-host([string]$filename)
{  
   $addip = "10.10.10.100"
   $addhostnames = "casarray.domain.local"
   foreach ($addhostname in $addhostnames)
       {
       $HostEntries = Get-Content $filename
       $Count = 0
       foreach ($line in $HostEntries)
           {
           $bits = [regex]::Split($line, "\t+")
           if ($bits.count -eq 2)
               {
               if ($bits[1] -eq $addhostname) {$Count+=1}                
               }
           }
       if($Count -eq 0)
           {
           Write-Host "Adding Host Entry" $addip "`t`t" $addhostname
           $addip + "`t`t" + $addhostname | Out-File -encoding ASCII -append $filename
           }
       else
           {
           Write-Host "Host Entries already exists"
           }
   }
}
function add-list([string]$ComputerList,[string]$ComputerName)
{
$ComputerNames = Get-Content $ComputerList
$count = 0
foreach($computer in $ComputerNames)
   {
   if ($computer -eq $ComputerName){$count+=1}
   }
if($count -eq 0)
   {
   $ComputerName | Add-Content $ComputerList
   Write-Host "Computer Name," $ComputerName "added in the list"
   }
   cmd /c pause
}
$date = Get-Date -Format "dd_MM_yyyy_hh_mm"
$computers = Get-Content "c:\Script\ComputerList.txt"
$ModifiedComputerNames = "C:\Script\ModifiedComputerNames.txt"
foreach ($computer in $computers)
{
$file = “\\”+ $computer + "\C$\Windows\System32\drivers\etc\hosts"
$fileCopy = “\\”+ $computer + "\C$\Windows\System32\drivers\etc\hosts.bak." + $date
cpi $file $fileCopy
add-host $file
add-list $ModifiedComputerNames $computer
}

You need to enter the list of computers for which the hosts file needs to be edited in the file c:\Script\ComputerList.txt. You may use a different path and file name, but remember to amend the script accordingly.

Script for Removing Entries to Hosts file

Following script will help you remove the temporarily added hosts entries for list of computers.

function remove-host([string]$filename )
{
$rmhostnames = "casarray.domain.local"
foreach ($rmhostname in $rmhostnames)
{
$c = Get-Content $filename
$newLines = @()
foreach ($line in $c)
{
$bits = [regex]::Split($line, "\t+")
if ($bits.count -eq 2)
   {
   if ($bits[1] -ne $rmhostname)
       {
       $newLines += $line
       }
   }
else
   {
   $newLines += $line
   }
}
# Write file
Clear-Content $filename
foreach ($line in $newLines)
   {
   $line | Out-File -encoding ASCII -append $filename
   Start-Sleep -m 100
   }
}
}
$computers = Get-Content "C:\Script\ModifiedComputerNames.txt"
foreach ($computer in $computers)
{
Write-Host $computer
$file = “\\”+ $computer + "\C$\Windows\System32\drivers\etc\hosts"
remove-host $file
}

In both the script, I have added a time wait to avoid network file editing errors. Which means, if the script executes really fast, the lock on the file might create writing errors. A slight time wait function will manage this situation efficiently.

Share your comments if any.

-Praveen

Published in Solutions

In my previous post, I have shared a script to Monitor Enterprise Vault Partition Growth / Size. If you are using that, it will be easy for you to understand this script as well. As you are aware, many of us run the pre and post backup script to make the EV servers ready for backup mode and release it. These scripts are normally scheduled or ran by the backup software. Regardless of the way it ran, the script provide a little information to the administrators about the status of store and index backup mode status.

This script is written to capture the backup mode status of all available store and Index location. You may schedule this script to run at a specified time, and verify the status at a glance and thus avoid regular check by logging into the EV server/Console.

Note - The Enterprise Vault scripts will only run on x86 powershell version. This limitation is because the commands from EV are not designed to work in 64 bit version of powershell. Ensure you run the script in x86 powershell.

Download the Script Here

The Full Script:

Import-Module "C:\Program Files (x86)\Enterprise Vault\Symantec.EnterpriseVault.PowerShell.AdminAPI.dll"

Import-Module "C:\Program Files (x86)\Enterprise Vault\Symantec.EnterpriseVault.PowerShell.Core.dll"

Import-Module "C:\Program Files (x86)\Enterprise Vault\Symantec.EnterpriseVault.PowerShell.IMAP.dll"

Import-Module "C:\Program Files (x86)\Enterprise Vault\Symantec.EnterpriseVault.PowerShell.Monitoring.dll"

$BkpFile = "C:\Scripts\HTML\Backup_Mode.htm" #Change the Value

$serverlist = (Get-EVComputers | select ComputerNameAlternate).ComputerNameAlternate

$evsite = (Get-EVSite | select Name).Name

$ModeValue = "False"

$date = ( Get-Date ).ToString('yyyy/MM/dd - hh:mm')

$smtphost = "yourSMTP_Server" #Change the Value

$from = "This email address is being protected from spambots. You need JavaScript enabled to view it." #Change the Value

$to = "This email address is being protected from spambots. You need JavaScript enabled to view it." #Change the Value

New-Item -ItemType file $BkpFile -Force

Function writeHtmlHeader

{

       param($fileName)

       Add-Content $fileName "<html>"

       Add-Content $fileName "<head>"

       Add-Content $fileName "<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'>"

       Add-Content $fileName '<title>Enterprise Vault MSMQ Monitor</title>'

       add-content $fileName '<STYLE TYPE="text/css">'

       add-content $fileName "<!--"

       add-content $fileName "td {"

       add-content $fileName "font-family: Tahoma;"

       add-content $fileName "font-size: 11px;"

       add-content $fileName "border-top: 1px solid #999999;"

       add-content $fileName "border-right: 1px solid #999999;"

       add-content $fileName "border-bottom: 1px solid #999999;"

       add-content $fileName "border-left: 1px solid #999999;"

       add-content $fileName "padding-top: 0px;"

       add-content $fileName "padding-right: 0px;"

       add-content $fileName "padding-bottom: 0px;"

       add-content $fileName "padding-left: 0px;"

       add-content $fileName "}"

       add-content $fileName "body {"

       add-content $fileName "margin-left: 5px;"

       add-content $fileName "margin-top: 5px;"

       add-content $fileName "margin-right: 0px;"

       add-content $fileName "margin-bottom: 10px;"

       add-content $fileName ""

       add-content $fileName "table {"

       add-content $fileName "border: thin solid #000000;"

       add-content $fileName "}"

       add-content $fileName "-->"

       add-content $fileName "</style>"

       Add-Content $fileName "</head>"

       Add-Content $fileName "<body>"

}

Function writeTableMemHeader

{

       param($fileName)

       Add-Content $fileName "<tr bgcolor=#B0AE5B>"

       Add-Content $fileName "<td width='10%' align='center'>Server/Site Name</td>"

       Add-Content $fileName "<td width='30%' align='center'>Index Location</td>"

       Add-Content $fileName "<td width='15%' align='center'>Backup Mode Status</td>"

       Add-Content $fileName "</tr>"

}

Function writeTableMemHeader2

{

       param($fileName)

       Add-Content $fileName "<tr bgcolor=#B0AE5B>"

       Add-Content $fileName "<td width='10%' align='center'>Server/Site Name</td>"

   Add-Content $fileName "<td width='30%' align='center'>Vault Store</td>"

   Add-Content $fileName "<td width='15%' align='center'>Backup Mode Status</td>"

       Add-Content $fileName "</tr>"

}

Function writeHtmlFooter

{

       param($fileName)

   Add-Content $fileName "</body>"

   Add-Content $fileName "<br/><br/>"

       Add-Content $fileName "</html>"

}

Function writememInfo

{

       param($fileName,$server, $IndexPath, $bkpmode)

      

       Add-Content $fileName "<tr>"

       Add-Content $fileName "<td>$server</td><td>$IndexPath</td>"

       If ("$bkpmode" -eq "False")

       {

              Add-Content $fileName "<td bgcolor='#086105' align=center>OFF</td>"

       }

       Else

       {

              Add-Content $fileName "<td bgcolor='#FF6B17' align=center>ON</td>"

       }            

       Add-Content $fileName "</tr>"

}

writeHtmlHeader $BkpFile

   add-content $BkpFile "<table width='55%'>"

       add-content $BkpFile "<tr bgcolor='#CCCCCC'>"

       add-content $BkpFile "<td colspan='3' height='25' align='center'>"

       add-content $BkpFile "<font face='tahoma' color='#003399' size='4'><strong>Enterprise Vault Backup Mode Monitor - $date</strong></font>"

       add-content $BkpFile "</td>"

       add-content $BkpFile "</tr>"

       add-content $BkpFile "</table>"

foreach ($server in $evsite)

{

       Add-Content $BkpFile "<table width='55%'><tbody>"

       Add-Content $BkpFile "<tr bgcolor='#CCCCCC'>"

       Add-Content $BkpFile "<td width='55%' align='center' colSpan=3><font face='tahoma' color='#003399' size='2'><strong> $server </strong></font></td>"

       Add-Content $BkpFile "</tr>"

       writeTableMemHeader $BkpFile

       $indexs = Get-IndexLocationBackupMode $server -EVSiteName archival | select-object indexrootpath, backupmode

       foreach ($index in $indexs) {

              $IndexPath = $index.IndexRootPath

              $bkpmode = $index.BackupMode

      

       writememInfo $BkpFile $server $IndexPath $bkpmode

       }

       #Add-Content $BkpFile "</table>"

  

   writeTableMemHeader2 $BkpFile

   $stores = Get-VaultStoreBackupMode -EVServerName $server -Name archival -EvObjectType Site | select-object VaultStoreName, BackupMode

       foreach ($store in $stores) {

              $storename = $store.VaultStoreName

              $bkpmode = $store.BackupMode            

      

       writememInfo $BkpFile $server $storename $bkpmode

       }

   Add-Content $BkpFile "</table>"

}

Writehtmlfooter $BkpFile

#Sending Email

   $subject = "Attention: Enterprise Vault HEALH CHECK REPORT"

   $body = Get-Content $BkpFile

   $smtp= New-Object System.Net.Mail.SmtpClient $smtphost

   $msg = New-Object System.Net.Mail.MailMessage $from, $to, $subject, $body

   $msg.isBodyhtml = $true

   $smtp.send($msg)

   # --------- Script Ends ----------------

Download the Script Here

Share your experience!

-Praveen

Published in Solutions

Enterprise Vault, one of the widely used/accepted solution for Email and File Archiving. The solution has improved drastically in version 11. However, it lack control in many area such as monitoring, partition size monitoring etc.

This script will help you to extract the existing size of all open partitions across all Enterprise Vault Servers. The script is tested on Enterprise Vault Ver 11.0. Download the complete script do few modification to work with your infra.

Note - The Enterprise Vault scripts will only run on x86 powershell version. This limitation is because the commands from EV are not designed to work in 64 bit version of powershell. Ensure you run the script in x86 powershell.

Download the Script Here

The Full Script:

Import-Module "C:\Program Files (x86)\Enterprise Vault\Symantec.EnterpriseVault.PowerShell.AdminAPI.dll"

Import-Module "C:\Program Files (x86)\Enterprise Vault\Symantec.EnterpriseVault.PowerShell.Core.dll"

Import-Module "C:\Program Files (x86)\Enterprise Vault\Symantec.EnterpriseVault.PowerShell.IMAP.dll"

Import-Module "C:\Program Files (x86)\Enterprise Vault\Symantec.EnterpriseVault.PowerShell.Monitoring.dll"

$PartitionSize = "C:\Scripts\HTML\Partition_Size.htm" #Update the path you wish to save the HTML output

$serverlist = (Get-EVComputers | select ComputerNameAlternate).ComputerNameAlternate

$evsite = (Get-EVSite | select Name).Name

$ModeValue = "False"

$date = ( Get-Date ).ToString('yyyy/MM/dd - hh:mm')

#Update with your infra details (Only if you wish to receive email notification)

$smtphost = "smtp.yourdomain.local"

$from = "This email address is being protected from spambots. You need JavaScript enabled to view it."

$to = "This email address is being protected from spambots. You need JavaScript enabled to view it."

 

New-Item -ItemType file $PartitionSize -Force

 

Function writeHtmlHeader 

{ 

       param($fileName) 

       Add-Content $fileName "<html>"

       Add-Content $fileName "<head>"

       Add-Content $fileName "<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'>"

       Add-Content $fileName '<title>Enterprise Vault MSMQ Monitor</title>'

       add-content $fileName '<STYLE TYPE="text/css">'

       add-content $fileName  "<!--"

       add-content $fileName  "td {"

       add-content $fileName  "font-family: Tahoma;"

       add-content $fileName  "font-size: 11px;"

       add-content $fileName  "border-top: 1px solid #999999;"

       add-content $fileName  "border-right: 1px solid #999999;"

       add-content $fileName  "border-bottom: 1px solid #999999;"

       add-content $fileName  "border-left: 1px solid #999999;"

       add-content $fileName  "padding-top: 0px;"

       add-content $fileName  "padding-right: 0px;"

       add-content $fileName  "padding-bottom: 0px;"

       add-content $fileName  "padding-left: 0px;"

       add-content $fileName  "}"

       add-content $fileName  "body {"

       add-content $fileName  "margin-left: 5px;"

       add-content $fileName  "margin-top: 5px;"

       add-content $fileName  "margin-right: 0px;"

       add-content $fileName  "margin-bottom: 10px;"

       add-content $fileName  ""

       add-content $fileName  "table {"

       add-content $fileName  "border: thin solid #000000;"

       add-content $fileName  "}"

       add-content $fileName  "-->"

       add-content $fileName  "</style>"

       Add-Content $fileName "</head>"

       Add-Content $fileName "<body>"

} 

 

Function writeHtmlFooter 

{ 

       param($fileName)

    Add-Content $fileName "</body>"

    Add-Content $fileName "<br/><br/>"

       Add-Content $fileName "</html>"

} 

 

 

Function writeTableMemHeader 

{ 

 

       param($fileName) 

       Add-Content $fileName "<tr bgcolor=#B0AE5B>"

       Add-Content $fileName "<td width='10%' align='center'>Server Name</td>"

       Add-Content $fileName "<td width='30%' align='center'>Folder Path</td>"

       Add-Content $fileName "<td width='15%' align='center'>Size in GB</td>"

       Add-Content $fileName "</tr>"

} 

 

 writeHtmlHeader $PartitionSize

 

       add-content $PartitionSize  "<table width='55%'>"

       add-content $PartitionSize  "<tr bgcolor='#CCCCCC'>"

       add-content $PartitionSize  "<td colspan='3' height='25' align='center'>"

       add-content $PartitionSize  "<font face='tahoma' color='#003399' size='4'><strong>Enterprise Vault Store Open Partition Size Monitor - $date</strong></font>"

       add-content $PartitionSize  "</td>"

       add-content $PartitionSize  "</tr>"

       add-content $PartitionSize  "</table>"

 

Function writememInfo 

{ 

       param($fileName,$server, $FolderName, $Size)

      

       Add-Content $fileName "<tr>"

       Add-Content $fileName "<td>$server</td><td>$FolderName</td>"

       If ("$Size" -gt "100")

       {

              Add-Content $fileName "<td bgcolor='#086105' align=center>$Size</td>"

       }

       Else

       {

              Add-Content $fileName "<td bgcolor='#FF6B17' align=center>$Size</td>"

       }            

       Add-Content $fileName "</tr>"

}

 

Function AddObject {

       Param ($FileObject, $FolderRef)

       $Size = [double]($FSO.GetFolder($FileObject.FullName).Size)

       $Script:TotSize += $Size

       If ($Size)

       {      $SizeInGB = CalculateSize $Size

       }

       Else

       {      $SizeInGB = "0.00"

        $Size = 0

       }

       $Script:Report += New-Object PSObject -Property @{

              'Folder Name' = $FileObject.FullName

              Size = $SizeInGB

       }

 writememInfo $PartitionSize $server $Folder $Report.Size

}

 

Function CalculateSize {

       Param ([double]$Size)

       If ($Size -gt 10000)

       {      $ReturnSize = "{0:N2}" -f ($Size / 1GB)

       }

       Else

       {      $ReturnSize = .01

       }

       Return $ReturnSize

}

 

    Add-Content $PartitionSize "<table width='55%'><tbody>"

       Add-Content $PartitionSize "<tr bgcolor='#CCCCCC'>"

       Add-Content $PartitionSize "<td width='55%' align='center' colSpan=3><font face='tahoma' color='#003399' size='2'><strong> $evsite </strong></font></td>"

       Add-Content $PartitionSize "</tr>"

    writeTableMemHeader $PartitionSize

 

foreach($server in $serverlist)

{

    $localhost = Get-content env:computername

    if ($server -ne $localhost)

    {

        $FolderList = Invoke-Command -ComputerName $server -ScriptBlock {

        Import-Module "C:\Program Files (x86)\Enterprise Vault\Symantec.EnterpriseVault.PowerShell.Core.dll"

        Get-EVVaultStorePartition | Where {$_.Status -eq "Open"}

    }

    $FolderList = $FolderList.Location

}

else

{

    $FolderList = Get-EVVaultStorePartition | Where {$_.Status -eq "Open"}

    $FolderList = $FolderList.Location

}

foreach($Folder in $FolderList)

{

    $Report = @()

    $TotSize = 0

    $FSO = New-Object -ComObject Scripting.FileSystemObject

    $UNCFolder = $Folder.Replace(':','$')

    $UNCFolder = "\\" + $server+ "\" + $UNCFolder

    $DirPath = Get-Item -Path $UNCFolder

    AddObject $DirPath $Folder

}

}

 

writeHtmlFooter $PartitionSize

 

 

#Sending Email

 

   $subject = "Attention: Enterprise Vault HEALH CHECK REPORT"

   $body =  Get-Content $PartitionSize

   $smtp= New-Object System.Net.Mail.SmtpClient $smtphost

   $msg = New-Object System.Net.Mail.MailMessage $from, $to, $subject, $body

   $msg.isBodyhtml = $true

   $smtp.send($msg)

Download the Script Here

Share your experience!

Note - Test before you apply on production server.

-Praveen

Published in Solutions

As you aware, the receive connectors are created on server identity, and we might end up in creating similar receive connector in multiple servers for redundancy/disaster recovery purposes. You can easily export and import the RemoteIPRages from one connector to other by just following the following simple steps,

First of all, create the receive connector on the server where you want to import the RemoteIPRanges (relay IPs). Ensure you do the necessary settings for the receive connector properties, as we only import the remoteIPRanges not all configurations’.

new-ReceiveConnector -Name 'connector_name' -Usage 'Custom' -Bindings '0.0.0.0:25' -RemoteIPRanges 'x.x.x.x' -Server 'new_server_name'

Now, we have to export the RemoteIPRanges values from the other server that we need to import to this newly created receive connector.

Get-ReceiveConnector "EXH2\ connector_name" | fl remote*

RemoteIPRange_Truncated-M

If the result is truncated as seen in the above figure, you can set the value $FormatEnumerationLimit to display the complete list. The default value for this variable is 16. Set the value to a higher limit, or as shown below to get the complete list.

$FormatEnumerationLimit =-1

FormatEnumerationLimit

Now execute the Get-ReceiveConnector "EXH2\ connector_name" | fl remote* cmdlet to get the complete the list of IP addresses allowed for realy.

Get_RemoteIPRange_Full

Copy the details into a notepad and format it by removing any additional space between the coma and the next IP address. This will look like below,

10.10.10.1,10.10.10.3,10.10.10.101-10.10.10.125

The input value also can contain the ranges as you may noticed, not only single IP addresses. Now you all set to import the data to the newly created Receive Connector, execute the below command (in the snapshot, I used separated IP ranges).

Set-ReceiveConnector -Identity "EXHDR1\ connector_name" –RemoteIPRanges 10.10.10.1,10.10.10.3,10.10.10.101-10.10.10.125

Set_RemoteIPRange_Full

That’s it, you have now imported the complete list of relay IPs to the newly created receive connector. This way is really useful when you have more number of IPs individually allowed to relay emails through a receive connector.

-Praveen

 

Published in Solutions
theme by reviewshub