Just can't get enough of IT

This blog is about mostly anything in IT. But the primary focuses are Microsoft Technologies like Exchange, Office 365, Azure and Cloud Security.

The PowerShell script to purge Exchange Server and IIS log files has been updated to version 2.1.

The function Copy-LogFiles has been slightly rewritten and there has been a change in the cmdlet parameters.

When using ArchiveMode CopyAndZip or CopyZipAndDelete all copied log files in the EXCHANGESERVER\LOGS folder are added to a compressed archive. The script creates a separate archive for IIS and Exchange logs.

Example of compressed archive files

 Code updated

function Copy-LogFiles {
    [string]$ArchivePrefix = ''

  if($SourceServer -ne '') { 

    # path per SERVER for zipped archives
    $ServerRepositoryPath = Join-Path -Path $RepositoryRootPath -ChildPath $SourceServer

    # subfolder used as target for copying source folders and files
    $ServerRepositoryLogsPath = Join-Path -Path $ServerRepositoryPath -ChildPath $LogSubfolderName

    $ServerRepositoryPath = Join-Path -Path $RepositoryRootPath -ChildPath $SourceServer

    if(!(Test-Path -Path $ServerRepositoryPath)) {

      # Create new target directory for server, if does not exist
      $null = New-Item -Path $ServerRepositoryPath -ItemType Directory -Force -Confirm:$false


    foreach ($File in $FilesToMove) {
      # target directory
      $targetDir = $File.DirectoryName.Replace($TargetServerFolder, $ServerRepositoryLogsPath)

      # target file path
      $targetFile = $File.FullName.Replace($TargetServerFolder, $ServerRepositoryLogsPath)
      # create target directory, if not exists
      if(!(Test-Path -Path $targetDir)) {$null = mkdir -Path $targetDir}

      # copy file to target
      $null = Copy-Item -Path $File.FullName -Destination $targetFile -Recurse -Force -Confirm:$false -ErrorAction SilentlyContinue

    if($ZipArchive) {
      # zip copied log files
      $Archive = Join-Path -Path $ServerRepositoryPath -ChildPath ('{0}-{1}' -f $ArchivePrefix, $ArchiveFileName)
      $logger.Write(('Zip copied files to {0}' -f $ArchiveFileName))
      # delete archive file, if already exists
      if(Test-Path -Path $Archive) {Remove-Item -Path $Archive -Force -Confirm:$false}

      try {
        # create zipped asrchive
        Add-Type -AssemblyName 'System.IO.Compression.FileSystem'
      catch {
        $logger.Write(('Error compressing files from {0} to {1}' -f $ServerRepositoryLogsPath, $Archive),3)      
      finally {

        # cleanup, if compression was successful
        if($DeleteZippedFiles) {

          $logger.Write(('Deleting folder {0}' -f $ServerRepositoryLogsPath))
          $null = Remove-Item -Path $ServerRepositoryLogsPath -Recurse -Force -Confirm:$false -ErrorAction SilentlyContinue








Read More »
On August 31, 2017
0 Comment


This script converts Word compatible documents to a selected format utilizing the Word SaveAs function. Each file is converted by a single dedicated Word COM instance.

The script converts either all documents in a single folder of a matching an include filter or a single file.

Currently supported target document types:

  • Default --> Word 2016
  • PDF
  • XPS
  • HTML

The conversion is handled by Word itself, utilizing the SaveAs method.

The Word.Document.SaveAs method supports the following wdFormat values:

Name Value Description
wdFormatDocument 0 Microsoft Office Word 97 - 2003 binary file format.
wdFormatDocument97 0 Microsoft Word 97 document format.
wdFormatTemplate 1 Word template format.
wdFormatTemplate97 1 Word 97 template format.
wdFormatText 2 Microsoft Windows text format.
wdFormatTextLineBreaks 3 Windows text format with line breaks preserved.
wdFormatDOSText 4 Microsoft DOS text format.
wdFormatDOSTextLineBreaks 5 Microsoft DOS text with line breaks preserved.
wdFormatRTF 6 Rich text format (RTF).
wdFormatEncodedText 7 Encoded text format.
wdFormatUnicodeText 7 Unicode text format.
wdFormatHTML 8 Standard HTML format.
wdFormatWebArchive 9 Web archive format.
wdFormatFilteredHTML 10 Filtered HTML format.
wdFormatXML 11 Extensible Markup Language (XML) format.
wdFormatXMLDocument 12 XML document format.
wdFormatXMLDocumentMacroEnabled 13 XML document format with macros enabled.
wdFormatXMLTemplate 14 XML template format.
wdFormatXMLTemplateMacroEnabled 15 XML template format with macros enabled.
wdFormatDocumentDefault 16 Word default document file format. For Word, this is the DOCX format.
wdFormatPDF 17 PDF format.
wdFormatXPS 18 XPS format.
wdFormatFlatXML 19 Open XML file format saved as a single XML file.
wdFormatFlatXML 20 Open XML file format with macros enabled saved as a single XML file.
wdFormatFlatXMLTemplate 21 Open XML template format saved as a XML single file.
wdFormatFlatXMLTemplateMacroEnabled 22 Open XML template format with macros enabled saved as a single XML file.
wdFormatOpenDocumentText 23 OpenDocument Text format.
wdFormatStrictOpenXMLDocument 24 Strict Open XML document format.

Implemented document formats are shown in bold.


# Convert all .doc files in E:\temp to Default

.\Convert-WordDocument.ps1 -SourcePath E:\Temp -IncludeFilter *.doc 

# Convert all .doc files in E:\temp to XPS

.\Convert-WordDocument.ps1 -SourcePath E:\Temp -IncludeFilter *.doc -TargetFormat XPS

# Convert a single document to Word default format

.\Convert-WordDocument.ps1 -SourcePath E:\Temp\MyDocument.doc

Version History

  • 1.0, Initial community release






Read More »
On August 19, 2017
0 Comment

The GlobalFunctions PowerShell module has been updated to support writing of log messages to the PowerShell output pipeline.

When writing to the PowerShell output pipeline, the severity level is not written to the pipeline. In most cases you will use this option for debugging purposes.


# Import module first
Import-Module -Name GlobalFunctions

# Create an instance of the logger
$ScriptDir = Split-Path -Path $script:MyInvocation.MyCommand.Path
$ScriptName = $MyInvocation.MyCommand.Name
$logger = New-Logger -ScriptRoot $ScriptDir -ScriptName $ScriptName -LogFileRetention 14 

# Write an informational message to the log file only
$logger.Write('Some informational message')

# Write an informational message to the log file only
$logger.Write('Some message to log and console',0,$true)

# Write a warning level message to log file and the message only to PowerShell output pipeline
$logger.Write('Some warning message',2,$true)


Remember to add the severity level when writing to console.


Read more about the GlobalFunctions module here.

You can get the source code here:

Read More »


This script deletes user from the NoSpamProxy NoSpamProxyAddressSynchronization database table [Usermanagement].[User] table that have not been removed by the NoSpamProxy Active Directory synchronization job.

The script was developed due to a process flaw in how Active Directory accounts are handled as part of a leaver process. So this script does not fix a software bug, but a process glitch.

Due to the Active Directory account process the accounts still exist in Active Directory and are synchronized to the NoSpamProxyAddressSynchronization database.

When executed without the -Delete parameter all identified users are wirtten the log file only.



# Check for Active Directory existance of all users stored in NoSpamProxy database. Do NOT delete any users from the database.


# Delete users from NoSpamProxy database hosted on SQL instance MYNSPSERVER\SQLEXPRESS that do NOT exist in Active Directory.

.\Remove-NspUsers.ps1 -Delete -SqlServerInstance MYNSPSERVER\SQLEXPRESS


Version History

  • 1.0, Initial community release





Read More »

Exchange Server 2013Exchange Server 2016Description

This scripts creates a new room mailbox and security two groups for full mailbox access and and for send-as delegation. The security groups are created using a configurable naming convention. If required by your Active Directory team, you can add group prefixes or department abbreviations as well.

The script uses a Xml configuration file to simplify changes for variables unique for your environment.

High level steps executes by the script:

  1. Create a new room mailbox
  2. Create a new mail enabled security group for full access delegation
  3. Assign full access security group for full access to the room mailbox
  4. Create a new mail enabled security group for send-as delegation
  5. Assign send-as permissions to send-as security group
  6. Set calendar processing to AutoAccept, if required
  7. Set resource capacity, if rewuired



Xml settings file

<?xml version="1.0"?>


The calendar booking security group feature is currently not available. But will be available in an upcoming release.

The following example creates a room mailbox for an Conference Room with empty security groups.

  -RoomMailboxName "MB - Conference Room" 
  -RoomMailboxDisplayName "Board Conference Room" 
  -RoomMailboxAlias "MB-ConferenceRoom" 
  -RoomMailboxSmtpAddress "" 
  -DepartmentPrefix "C"

You can simplify the use of the script by using a paramterized helper script named Create-RoomMailbox.ps1.

The following Create-RoomMailbox.ps1 script simplifies the process of creating a team mailbox even more.

$roomMailboxName = 'MB-Conference Room'
$roomMailboxDisplayName = 'Board Conference Room'
$roomMailboxAlias = 'MB-ConferenceRoom'
$roomMailboxSmtpAddress = ''
$departmentPrefix = 'C'
$groupFullAccessMembers = @('JohnDoe','JaneDoe')  # Empty = @()
$groupSendAsMember = @()
$groupCalendarBooking = @()
$RoomCapacity = 0
$RoomList = 'AllRoomsHQ'

  -RoomMailboxName $roomMailboxName 
  -RoomMailboxDisplayName $roomMailboxDisplayName 
  -RoomMailboxAlias $roomMailboxAlias 
  -RoomMailboxSmtpAddress $roomMailboxSmtpAddress 
  -DepartmentPrefix $departmentPrefix 
  -GroupFullAccessMembers $groupFullAccessMembers 
  -GroupSendAsMember $groupSendAsMember 
  -RoomCapacity $RoomCapacity 
  -RoomList $RoomList

Version History

  • 1.0, Initial community release





Read More »