Added a parameter and now I'm getting ParameterBindingValidationException errors

I’ve added a parameter to a script and I’m now getting ParameterBindingValidationException errors on cmdlets like test-path, and out-file, presumably because the files they refer to don’t exist yet. They shouldn’t, or don’t need to exist until written to.

I’ve added a single parameter, and it’s now validating the paths. What are my options for handling it?

[pre]Param
(

The batchnumber is a two digit number that is used in the filename of the batch CSV (E.G. 09).

This assumes the standard file name format is “PhaseXX_Mailboxes.csv”, where the batchnumber replaces “XX”

[Parameter(Mandatory=$true,Position=0)]
[ValidatePattern(“[0-9][0-9]”)]
[string]$batchnumber
)[/pre]

Then in the script I have lines as follows that generate the errors:

[pre]“$(Get-Date -Format “yyyy-MM-dd HH:mm:ss”), Batch:$Batchnumber , User:$I, $($MigraMB.PrimarySMTPAddress)” | out-file $LogPath -Append[/pre]

and

[pre] If(test-path $ErrorLogPath)[/pre]

Errors:

Test-Path : Cannot bind argument to parameter ‘Path’ because it is null.
At C:\scripts\O365-Migration-DuplicateSendAsToEXO\O365-Migration-DuplicateSendAsToEXO.ps1:196 char:18

  • If(Test-Path $ErrorLogPath)
  • CategoryInfo : InvalidData: (:slight_smile: [Test-Path], ParameterBindingValidationException
  • FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.TestPathCommand

Out-File : Cannot bind argument to parameter ‘FilePath’ because it is null.
At C:\scripts\O365-Migration-DuplicateSendAsToEXO\O365-Migration-DuplicateSendAsToEXO.ps1:192 char:105

  • … $E" | out-file $ErrorLogPath -Append}
  • CategoryInfo : InvalidData: (:slight_smile: [Out-File], ParameterBindingValidationException
  • FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.OutFileCommand

 

Could you post more of the script? I think the errors you are getting refer to the parameters used in your

Out-File

and
Test-Path

cmdlets. Not the parameter you added to your script.

Steve

Here are the variables at the beginning of the script that set up the paths. The error log won’t be created if there aren’t any.

[pre]

$PathRoot = "C:\scripts"

$D = Get-Date -Format “yyyy-MM-dd_HH-mm”
$LogPath = (Join-Path $PathRoot -ChildPath “Logs\all_LOG_$D.txt”)
$MBoutFile = (Join-Path $PathRoot -ChildPath “Data\ALL_$D.csv”)
$MigrationUsersFile = (Join-Path $PathRoot -ChildPath “Batches\Phase$($Batchnumber)Mailboxes.csv")
$SendAsPermsFile = (Join-Path $PathRoot -ChildPath "Data\ALL-SendAs
$D.csv”)
$ExportObjectFile = (Join-Path $PathRoot -ChildPath “Logs\all_LOG_$D.csv”)
$TranscriptLogPath = (Join-Path $PathRoot -ChildPath “Logs\all-Transcript_$D.txt”)
$ErrorLogPath = (Join-Path $PathRoot -ChildPath “Logs\all_ERRORS_$D.log”)

[/pre]

One of the errors is on the test-path line of the following:

[pre]

FINALLY
{
If(test-path $ErrorLogPath)
{
$FE = GC $ErrorLogPath | Out-String
Send-MailMessage -TO “me@domain.com” -From “him@domain.com” `
-SmtpServer server1.domain.com
-Subject “O365-SendAsDuplicater Completed with Errors: $(Get-Date -Format “yyyy-MM-dd HH:mm:ss”)” `
-body “Check Error log on server $($env:computername): `n: $($ErrorLogPath) `n $($FE)”
}
ELSE
{
Send-MailMessage -TO “me@domain.com” -From “him@domain.com” `
-SmtpServer server1.domain.com
-Subject “Duplicater Completed without Errors: $(Get-Date -Format “yyyy-MM-dd HH:mm:ss”)” `
-body “Completed on server $($env:computername)”
}

} # FINALLY END

[/pre]

Another error is on the scripts overall try\catch, although there are other “out-file $ErrorLogPath -Append” lines within the body. That makes it seems that it’s validating paths associated with the overall try\catch\finally, and not the body of the script. Does putting in a single parameter force it to validate those paths, or does having a paramater with a validation attribute force that?

[pre]}CATCH{$E = $_ | select Exception;“$(Get-Date -Format “yyyy-MM-dd HH:mm:ss”) Error Main: $E” | out-file $ErrorLogPath -Append}[/pre]

It seems that your path variables aren’t being assigned at all. Is the section of your script that assigns the paths to variables inside a function?

Here’s the entire script cleaned up…I hope. The variables are inside the main Try\Catch, but not in a separate function. I could move them outside the Try\Catch, which is what the errors are on.

[pre]

TRY{

VARIABLES START

*** CHANGE $Prefix FOR EACH BATCH ***

$Prefix = “HERE”
$PSTnames = (get-Item “\server1\c$\PSTs\UPload$Prefix-*”).name

*** Change the $PathRoot as needed ***

$PathRoot = “C:\scripts\import\Logs”
$D = Get-Date -Format “yyyy-MM-dd_HH-mm-ss”

sl $PSScriptRoot

These strings will be removed…

$ReplaceFileString1 = “$($Prefix)-”
$ReplaceFileString2 = “_Export_0001”
$ReplaceFileString3 = “.pst”

Log paths

$LogPath = (Join-Path $PathRoot -ChildPath “Purge_General.log”)
$ErrorLogPath = (Join-Path $PathRoot -ChildPath “Purge_ERRORS_$D.log”)
$MBstatsBeforeTagPurge = (Join-Path $PathRoot -ChildPath “Purge_MBstats-BEFORE.csv”)
$MBstatsAfterTagPurge = (Join-Path $PathRoot -ChildPath “Purge_MBstats-AFTER.csv”)
$ItemsReport = (Join-Path $PathRoot -ChildPath “Purge_Report.log”)
$TranscriptLogPath = (Join-Path $PathRoot -ChildPath “Transcripts\Purge-Transcript_$D.txt”)
Start-Transcript -Path $TranscriptLogPath -IncludeInvocationHeader -NoClobber

VARIABLES END

FUNCTION START

Function to set…

TRY{

Function TagNeverDelete ($Mailbox)
{

Set the mailbox SMTP for the Service

$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress,$Mailbox)

Set up search criteria to find the “MaFold” and “HistItems” folders

$FolderView = new-object Microsoft.Exchange.WebServices.Data.FolderView(1)
$FolderView.Traversal = [Microsoft.Exchange.Webservices.Data.FolderTraversal]::Shallow
$SearchFilterManagedFolders = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,“MaFold”)
$SearchFilterHistItems = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,“HistItems”)

Create the properties needed to apply the personal tag: PR_POLICY_TAG [0x3019], PR_RETENTION_FLAGS [0x301D], PR_RETENTION_PERIOD [0x301A]

$PolicyTag = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x3019,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary);
$RetentionFlags = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x301D,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer);
$RetentionPeriod = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x301A,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer);

Set up the personal tag…

$PolicyTagGUID = new-Object Guid(“{674c6a14-3ed5-432e-9edb-c6620a8278f0}”);

######### Primary Mailbox START #########

Search for the “MaFold” folder

$FindFolderResultsPrimaryManagedFolders = $service.FindFolders([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$SearchFilterManagedFolders,$FolderView)

If the “MaFold” folder is NOT FOUND add a log entry and take no action

if ($FindFolderResultsPrimaryManagedFolders.TotalCount -eq 0)
{
Write-host “MaFold (PRIMARY) DOES NOT EXIST: $($Mailbox)”
“$(Get-Date -Format “yyyy-MM-dd HH:mm:ss”) MaFold (PRIMARY) DOES NOT EXIST: $($Mailbox)” | out-file $LogPath -Append
}

If the “MaFold” folder is FOUND look for subfolder “HistItems”

else
{

Search for subfolder “HistItems”

$FindFolderResultsPrimaryHistItems = $service.FindFolders($FindFolderResultsPrimaryManagedFolders.id,$SearchFilterHistItems,$FolderView)

If the “HistItems” folder is NOT FOUND add a log entry and take no action

if ($FindFolderResultsPrimaryHistItems.TotalCount -eq 0)
{
Write-host “HistItems (PRIMARY) DOES NOT EXIST: $($Mailbox)”
“$(Get-Date -Format “yyyy-MM-dd HH:mm:ss”) HistItems (PRIMARY) DOES NOT EXIST: $($Mailbox)” | out-file $LogPath -Append
}

If the “HistItems” folder is FOUND assign the never delete tag

Else
{

Bind to “HistItems” folder

$Folder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$FindFolderResultsPrimaryHistItems.Folders[0].Id)

Set property values: PR_POLICY_TAG [0x3019], PR_RETENTION_FLAGS [0x301D], PR_RETENTION_PERIOD [0x301A]

$Folder.SetExtendedProperty($RetentionFlags, 137)
$Folder.SetExtendedProperty($RetentionPeriod, 0)
$Folder.SetExtendedProperty($PolicyTag, $PolicyTagGUID.ToByteArray())

Apply the changes to the folder

$Folder.Update()

Write-host “Retention policy stamped on HistItems (PRIMARY): $($Mailbox)”
“$(Get-Date -Format “yyyy-MM-dd HH:mm:ss”) Retention policy stamped on HistItems (PRIMARY): $($Mailbox)” | out-file $LogPath -Append

} # else Inner Primary Mailbox END

} # else Outer Primary Mailbox END

######### Primary Mailbox END #########

######### Archive Mailbox START #########

Search for the “MaFold” folder

$FindFolderResultsArchiveManagedFolders = $service.FindFolders([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::ArchiveMsgFolderRoot,$SearchFilterManagedFolders,$FolderView)

If the “MaFold” folder is NOT FOUND add a log entry and take no action

if ($FindFolderResultsArchiveManagedFolders.TotalCount -eq 0)
{
Write-host “MaFold (ARCHIVE) DOES NOT EXIST: $($Mailbox)”
“$(Get-Date -Format “yyyy-MM-dd HH:mm:ss”) MaFold (ARCHIVE) DOES NOT EXIST: $($Mailbox)” | out-file $LogPath -Append
}

If the “MaFold” folder is FOUND look for subfolder “HistItems”

else
{

Search for subfolder “HistItems”

$FindFolderResultsArchiveHistItems = $service.FindFolders($FindFolderResultsArchiveManagedFolders.id,$SearchFilterHistItems,$FolderView)

If the “HistItems” folder is NOT FOUND add a log entry and take no action

if ($FindFolderResultsArchiveHistItems.TotalCount -eq 0)
{
Write-host “HistItems (ARCHIVE) DOES NOT EXIST: $($Mailbox)”
“$(Get-Date -Format “yyyy-MM-dd HH:mm:ss”) HistItems (ARCHIVE) DOES NOT EXIST: $($Mailbox)” | out-file $LogPath -Append
}

If the “HistItems” folder is FOUND assign the never delete tag

Else
{

Bind to “HistItems” folder

$Folder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$FindFolderResultsArchiveHistItems.Folders[0].Id)

Set property values: PR_POLICY_TAG [0x3019], PR_RETENTION_FLAGS [0x301D], PR_RETENTION_PERIOD [0x301A]

$Folder.SetExtendedProperty($RetentionFlags, 137)
$Folder.SetExtendedProperty($RetentionPeriod, 0)
$Folder.SetExtendedProperty($PolicyTag, $PolicyTagGUID.ToByteArray())

Apply the changes to the folder

$Folder.Update()

Write-host “Retention policy stamped on HistItems (ARCHIVE): $($Mailbox)”
“$(Get-Date -Format “yyyy-MM-dd HH:mm:ss”) Retention policy stamped on HistItems (ARCHIVE): $($Mailbox)” | out-file $LogPath -Append

} # else Inner Archive Mailbox END

} # else Outer Archive Mailbox END

######### Archive Mailbox END #########

Make sure the user is NULLed out for good measure before processing the next

$service.ImpersonatedUserId = $null

} # Function TagNeverDelete END

}CATCH{$E = $_ | select Exception;“$(Get-Date -Format “yyyy-MM-dd HH:mm:ss”) Error Function TagNeverDelete: $($E)” | out-file $ErrorLogPath -Append}

FUNCTION END

Create credential object using pass hash file previously created:

TRY{
$Username = “user@domain.com
$Password = Get-Content “C:\HashFile.pwd” | ConvertTo-SecureString
$Credential = New-Object System.Management.Automation.PSCredential($Username,$Password)
}CATCH{$E = $_ | select Exception;“$(Get-Date -Format “yyyy-MM-dd HH:mm:ss”) Error Credential Create: $($E)” | out-file $ErrorLogPath -Append}

Connect to EXO. Import only necessary Exchange cmdlets.

#########################################################################################################################
TRY{
$SessionEXO = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $Credential -Authentication Basic -AllowRedirection
$ImportResultsEXO = Import-PSSession $SessionEXO -Prefix EXO -CommandName “Get-Mailbox”, “start-managedfolderassistant”, “Get-MailboxFolderStatistics” -DisableNameChecking
}CATCH{$E = $_ | select Exception;“$(Get-Date -Format “yyyy-MM-dd HH:mm:ss”) Error Connect Exchange EXO: $($E)” | out-file $ErrorLogPath -Append}
#########################################################################################################################

Import “Microsoft Exchange Web Services Managed API 2.2” to adjust retention properties

DOWNLOAD: https://www.microsoft.com/en-us/download/details.aspx?id=42951

TRY{
Import-Module -Name “C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll”
}CATCH{$E = $_ | select Exception;“$(Get-Date -Format “yyyy-MM-dd HH:mm:ss”) Error Import WebServices Module: $($E)” | out-file $ErrorLogPath -Append}

TagNeverDelete service setup: Create the service object to access the mailboxes

$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService -ArgumentList Exchange2013_SP1
$service.Credentials = new-object Microsoft.Exchange.WebServices.Data.WebCredentials -ArgumentList $Credential.UserName, $Credential.GetNetworkCredential().Password
$service.Url= new-object Uri(“https://outlook.office365.com/EWS/Exchange.asmx”)
$service.TraceEnabled = $true

Intiate array to collect the mailbox UPNs

$UPNs = @()

For each mailbox…

foreach($PSTname in $PSTnames)
{
$UserDisplayName = $PSTname -replace (“$ReplaceFileString1”,“”) -replace (“$ReplaceFileString2”,“”) -replace (‘,’,', ') -replace (“$ReplaceFileString3”,“”)

$UserDisplayName

Get the Mailbox UPN, and add them to an array to later use to start the MFA for each mailbox

TRY{
$UPN = (Get-EXOMailbox $UserDisplayName).userprincipalname

$UPNs += $UPN

$StatsArchive = Get-EXOMailboxFolderStatistics $UPN -Archive | sort Name |
select @{n=‘DATE’;e={Get-Date -Format “yyyy-MM-dd HH:mm:ss”}},@{n=‘MBtype’;e={“Archive”}}, Identity, FolderPath, Name, DeletePolicy, ArchivePolicy, CreationTime, LastModifiedTime, FolderType, FolderSize, FolderAndSubfolderSize, ItemsInFolder, VisibleItemsInFolder, HiddenItemsInFolder, DeletedItemsInFolder, ItemsInFolderAndSubfolders, DeletedItemsInFolderAndSubfolders, FolderId
$StatsArchive | Export-Csv $MBstatsBeforeTagPurge -NoTypeInformation -Append -Force

$StatsPrimary = Get-EXOMailboxFolderStatistics $UPN | sort Name |
select @{n=‘DATE’;e={Get-Date -Format “yyyy-MM-dd HH:mm:ss”}},@{n=‘MBtype’;e={“Primary”}}, Identity, FolderPath, Name, DeletePolicy, ArchivePolicy, CreationTime, LastModifiedTime, FolderType, FolderSize, FolderAndSubfolderSize, ItemsInFolder, VisibleItemsInFolder, HiddenItemsInFolder, DeletedItemsInFolder, ItemsInFolderAndSubfolders, DeletedItemsInFolderAndSubfolders, FolderId
$StatsPrimary | Export-Csv $MBstatsBeforeTagPurge -NoTypeInformation -Append -Force

}CATCH{$E = $_ | select Exception;“$(Get-Date -Format “yyyy-MM-dd HH:mm:ss”) Error on Get-EXOMailbox for $($UPN): $($E)” | out-file $ErrorLogPath -Append}

Assign never delete to “HistItems” folder

TRY{
Write-host “`n”

“`n” | out-file $LogPath -Append

TagNeverDelete -Mailbox $UPN

Write-host “`n”

“`n” | out-file $LogPath -Append

}CATCH{$E = $_ | select Exception;“$(Get-Date -Format “yyyy-MM-dd HH:mm:ss”) Error on TagNeverDelete function for $($UPN): $($E)” | out-file $ErrorLogPath -Append}

Remove…

TRY{
“`n******” | out-file $ItemsReport -Append
“$(Get-Date -Format “yyyy-MM-dd HH:mm:ss”): -START- ItemsReport for $($UPN)” | out-file $ItemsReport -Append
" | out-file $ItemsReport -Append
.\Remove-MessageClassItems.ps1 $UPN -ArchiveOnly -ScanAllFolders -MessageClass ‘IPM.NOTE.type1.Shortcut’ -Credentials $Credential -Impersonation -Server outlook.office365.com -Verbose -Confirm:$false <#-WhatIf:$true#> >> $ItemsReport
"`n
" | out-file $ItemsReport -Append
“$(Get-Date -Format “yyyy-MM-dd HH:mm:ss”): -END- ItemsReport for $($UPN)” | out-file $ItemsReport -Append
"
*****” | out-file $ItemsReport -Append

}CATCH{$E = $_ | select Exception;“$(Get-Date -Format “yyyy-MM-dd HH:mm:ss”) Error on Remove-MessageClassItems for $($UPN): $($E)” | out-file $ErrorLogPath -Append}

} # foreach($PSTname… END

sleep 20

Start Managed Folder Assistant to apply newly assigned tag

Foreach($UPN in $UPNs) #{$UPN}
{

Start Managed Folder Assistant to apply tag

TRY{

start-exomanagedfolderassistant $UPN # -WhatIf

Write-host “MaFold Assistant has been run for: $($UPN)” #-foregroundcolor $info
“$(Get-Date -Format “yyyy-MM-dd HH:mm:ss”) MaFold Assistant has been run for: $($UPN)” | out-file $LogPath -Append

}CATCH{$E = $_ | select Exception;“$(Get-Date -Format “yyyy-MM-dd HH:mm:ss”) Error on start-exomanagedfolderassistant for $($UPN): $($E)” | out-file $ErrorLogPath -Append}

} # Foreach($U… END

sleep 300

Get Mailbox Folder stats after changes

Foreach($UPN in $UPNs) #{$UPN}
{
$StatsArchive = Get-EXOMailboxFolderStatistics $UPN -Archive | sort Name |
select @{n=‘DATE’;e={Get-Date -Format “yyyy-MM-dd HH:mm:ss”}},@{n=‘MBtype’;e={“Archive”}}, Identity, FolderPath, Name, DeletePolicy, ArchivePolicy, CreationTime, LastModifiedTime, FolderType, FolderSize, FolderAndSubfolderSize, ItemsInFolder, VisibleItemsInFolder, HiddenItemsInFolder, DeletedItemsInFolder, ItemsInFolderAndSubfolders, DeletedItemsInFolderAndSubfolders, FolderId
$StatsArchive | Export-Csv $MBstatsAfterTagPurge -NoTypeInformation -Append -Force

$StatsPrimary = Get-EXOMailboxFolderStatistics $UPN | sort Name |
select @{n=‘DATE’;e={Get-Date -Format “yyyy-MM-dd HH:mm:ss”}},@{n=‘MBtype’;e={“Primary”}}, Identity, FolderPath, Name, DeletePolicy, ArchivePolicy, CreationTime, LastModifiedTime, FolderType, FolderSize, FolderAndSubfolderSize, ItemsInFolder, VisibleItemsInFolder, HiddenItemsInFolder, DeletedItemsInFolder, ItemsInFolderAndSubfolders, DeletedItemsInFolderAndSubfolders, FolderId
$StatsPrimary | Export-Csv $MBstatsAfterTagPurge -NoTypeInformation -Append -Force

} # Foreach($UPN… END

Stop-Transcript

}CATCH{$E = $_ | select Exception;“$(Get-Date -Format “yyyy-MM-dd HH:mm:ss”) Error Caught Main: $($E)” | out-file $ErrorLogPath -Append}

FINALLY
{
If(Test-Path $ErrorLogPath)
{
$FE = GC $ErrorLogPath | Out-String
Send-MailMessage -TO “who@domain.com” -From “Purge@domain.com” `
-SmtpServer server2.domain.com
-Subject “Purge Completed with Errors: $(Get-Date -Format “yyyy-MM-dd HH:mm:ss”)” `
-body “Check Error log on server $($env:computername): `n: $($ErrorLogPath) `n $($FE)”
}
ELSE
{
Send-MailMessage -TO “who@domain.com” -From “Purge@domain.com” `
-SmtpServer server2.domain.com
-Subject “Purge Completed without Errors: $(Get-Date -Format “yyyy-MM-dd HH:mm:ss”)” `
-body “Completed on server $($env:computername)”
}

} # FINALLY END

[/pre]

Move the variables outside the Try\Catch stops the errors from happening. I guess that’s fine, but it doesn’t anser why the paths are erroring.

When the parameter is in it’s own try\catch with the validation attribute it errors on the same 2 lines in the Catch\Finally that is for the body of the script. When I remove the validation attribute of the parameter it errors on the Catch at the end of the variable section. So, it seems if you have a parameter within a TRY\CATCH it will validate the Catch and Finally, which includes any paths in them, prior to doing anything within the TRY\CATCH. And having a validation attribute changes the behaviour of the validations. This must be known behaviour?

This has the parameter validation attribute, and errors on the CATCH\FINALLY that is for the script body, not on the CATCH that holds the parameter and variables:

[pre]

TRY{

Param
(
[Parameter(Mandatory=$true,Position=0)]
[ValidatePattern(“[1][1-9]$”)]
$batchnumber
)

VARIABLES START

*** CHANGE $Prefix FOR EACH BATCH ***

$Prefix = “HERE”
$PSTnames = (get-Item “\server1\c$\PSTs\UPload$Prefix-*”).name

*** Change the $PathRoot as needed ***

$PathRoot = “C:\scripts\import\Logs”
$D = Get-Date -Format “yyyy-MM-dd_HH-mm-ss”

sl $PSScriptRoot

These strings will be removed…

$ReplaceFileString1 = “$($Prefix)-”
$ReplaceFileString2 = “_Export_0001”
$ReplaceFileString3 = “.pst”

Log paths

$LogPath = (Join-Path $PathRoot -ChildPath “Purge_General.log”)
$ErrorLogPath = (Join-Path $PathRoot -ChildPath “Purge_ERRORS_$D.log”)
$MBstatsBeforeTagPurge = (Join-Path $PathRoot -ChildPath “Purge_MBstats-BEFORE.csv”)
$MBstatsAfterTagPurge = (Join-Path $PathRoot -ChildPath “Purge_MBstats-AFTER.csv”)
$ItemsReport = (Join-Path $PathRoot -ChildPath “Purge_Report.log”)
$TranscriptLogPath = (Join-Path $PathRoot -ChildPath “Transcripts\Purge-Transcript_$D.txt”)
Start-Transcript -Path $TranscriptLogPath -IncludeInvocationHeader -NoClobber

VARIABLES END

}CATCH{$E = $_ | select Exception;“$(Get-Date -Format “yyyy-MM-dd HH:mm:ss”) Error on variablese: $($E)” | out-file $ErrorLogPath -Append}

[/pre]

For this the parameter has no validation attribute, and only errors on the CATCH at the end of the Parameter\variable, not on the script body CATCH\FINALLY:

[pre]

TRY{
Param
(
[Parameter(Mandatory=$true,Position=0)]
$batchnumber
)

VARIABLES START

*** CHANGE $Prefix FOR EACH BATCH ***

$Prefix = “HERE”
$PSTnames = (get-Item “\server1\c$\PSTs\UPload$Prefix-*”).name

*** Change the $PathRoot as needed ***

$PathRoot = “C:\scripts\import\Logs”
$D = Get-Date -Format “yyyy-MM-dd_HH-mm-ss”

sl $PSScriptRoot

These strings will be removed…

$ReplaceFileString1 = “$($Prefix)-”
$ReplaceFileString2 = “_Export_0001”
$ReplaceFileString3 = “.pst”

Log paths

$LogPath = (Join-Path $PathRoot -ChildPath “Purge_General.log”)
$ErrorLogPath = (Join-Path $PathRoot -ChildPath “Purge_ERRORS_$D.log”)
$MBstatsBeforeTagPurge = (Join-Path $PathRoot -ChildPath “Purge_MBstats-BEFORE.csv”)
$MBstatsAfterTagPurge = (Join-Path $PathRoot -ChildPath “Purge_MBstats-AFTER.csv”)
$ItemsReport = (Join-Path $PathRoot -ChildPath “Purge_Report.log”)
$TranscriptLogPath = (Join-Path $PathRoot -ChildPath “Transcripts\Purge-Transcript_$D.txt”)
Start-Transcript -Path $TranscriptLogPath -IncludeInvocationHeader -NoClobber

VARIABLES END

}CATCH{$E = $_ | select Exception;“$(Get-Date -Format “yyyy-MM-dd HH:mm:ss”) Error on variablese: $($E)” | out-file $ErrorLogPath -Append}
[/pre]

This doesn’t error at all, The paramter is not in a TRY\CATCH:

[pre]
Param
(
[Parameter(Mandatory=$true,Position=0)]
[ValidatePattern(“[2][1-9]$”)]
$batchnumber
)

TRY{

VARIABLES START

*** CHANGE $Prefix FOR EACH BATCH ***

$Prefix = “HERE”
$PSTnames = (get-Item “\server1\c$\PSTs\UPload$Prefix-*”).name

*** Change the $PathRoot as needed ***

$PathRoot = “C:\scripts\import\Logs”
$D = Get-Date -Format “yyyy-MM-dd_HH-mm-ss”

sl $PSScriptRoot

These strings will be removed…

$ReplaceFileString1 = “$($Prefix)-”
$ReplaceFileString2 = “_Export_0001”
$ReplaceFileString3 = “.pst”

Log paths

$LogPath = (Join-Path $PathRoot -ChildPath “Purge_General.log”)
$ErrorLogPath = (Join-Path $PathRoot -ChildPath “Purge_ERRORS_$D.log”)
$MBstatsBeforeTagPurge = (Join-Path $PathRoot -ChildPath “Purge_MBstats-BEFORE.csv”)
$MBstatsAfterTagPurge = (Join-Path $PathRoot -ChildPath “Purge_MBstats-AFTER.csv”)
$ItemsReport = (Join-Path $PathRoot -ChildPath “Purge_Report.log”)
$TranscriptLogPath = (Join-Path $PathRoot -ChildPath “Transcripts\Purge-Transcript_$D.txt”)
Start-Transcript -Path $TranscriptLogPath -IncludeInvocationHeader -NoClobber

VARIABLES END

}CATCH{$E = $_ | select Exception;“$(Get-Date -Format “yyyy-MM-dd HH:mm:ss”) Error on variablese: $($E)” | out-file $ErrorLogPath -Append}
[/pre]


  1. 0-9 ↩︎

  2. 0-9 ↩︎

Pretty sure your parameter block cannot be inside a Try/Catch. I’m not really sure why you would want it to be. In addition, you have a function definition inside a try/catch block. Again, not sure why that is the case either??

Also - FWIW, there are a lot of nested try/catch blocks here, and with your code formatted left justified it makes it a little difficult to follow. If you use vscode as your editor, a couple of simple settings will auto-format your code for easier readability. And finally, it is more of a best practice / pattern to use parameter splatting vs back ticks when sending longer parameter sets into a cmdlet - like where you are using your Send-Mail.

I’m a hack I suppose. I’ve read up and watched a number of MS instructional videos on powershell a number of years ago, but since then I figure it out as I go, and read up on certain things, which leaves me behind, forgetting certain things, and not in the know of many best practices. One practice I found useful was to have those nested try\catch blocks, and the code in them is indented in them in the script. It didn’t copy that way apparently. I didn’t give it much thought about having the parameters in a TRY\CATCH. I don’t use parameters much to tell the truth, and I’m used to putting the entire script in a TRY\CATCH so that’s what happened. The nested function is there for the same reason. I didn’t want to separate it from the main script as a separate file.

I’ve tried vscode, but I always connect to both on-prem and exchange online with remote powershell, and the editor window does not allow me to use both sets of cmdlets even though, as is my normal practice, I set a prefix for one of them. the editor window will only resolve one set of cmdlets (e.g. on-prem), and not the other. I posted about that in stackoverflow, and couldn’t get a satisfactory solution. So, I continue to use ISE. I’ve used splatting before. That’s one of those things where I’ve just copied from another script, but I can change the way i do that.

Thank you for the feedback

Aren’t we all? :slight_smile:

While I am sure that some would say pulling that function out of the file into a separate ps1 file - I’m not sure it would be completely necessary. Typically when I have defined functions within a script, I try to do it at the top (below the param block if there is one). The only rule there is that the function needs to be defined before you use it.

Happy to provide, hope it helped.

Steve