Help with refactoring code to use parameters and/or functions

I’m building a script that creates a folder and file structure for projects and I’m in the process of refactoring the code to use parameters and/or functions and have run into an issue right away. I’ve spent way too much time already trying to solve this and I’m hoping you can help me figure this out.

What I want is this:

  • Ask for the name of the project
  • Ask for the name of the author
  • The names should match two different RegEx patterns as seen in the script below
  • If the names doesn't match the RegEx it should show a message with information about allowed characters
  • After X (currently 3) amount of retries it should throw a customized error message and stop the execution
  • The variables $ProjectName and $AuthorName must of course be able to be used later in the script (something that I've had trouble with so far in my tests)
In the code below you can see my custom loop which is based on this that does all of the above. I'm primarily looking at turning these into parameters so that they could be passed inline like >ProjectBuilder.ps1 -ProjectName Test -AuthorName John Doe but I'm open to suggestions regarding best practices etc. Maybe they could (should?) be separated so the validation happens in a separate function?
$StopLoop = $false
[int]$Retrycount = "0"
Do{
    Try {
        [ValidatePattern('^[a-zA-Z0-9\-]{1,40}$')]$ProjectName = Read-Host 'What is the name of this project?'
        $StopLoop = $true
    }
    Catch {
        if ($Retrycount -gt 1) {
            Write-Host 'Too many retries. Exiting' -ForegroundColor Red
            $StopLoop = $true
            Exit
        }
        else { 
            $Retrycount = $Retrycount + 1
            Write-Host 'Invalid project name. Try again. Name must be between 1-40 characters and only allows these characters: a-z, A-Z, 0-9 and -' -ForegroundColor Yellow
        }        
    }
} While ($StopLoop -eq $false)

$StopLoop = $false
[int]$Retrycount = “0”
Do{
Try {
[ValidatePattern(’^.{1,40}$’)]$AuthorName = Read-Host ‘Who is the author of this project?’
$StopLoop = $true
}
Catch {
if ($Retrycount -gt 1) {
Write-Host ‘Too many retries. Exiting’ -ForegroundColor Red
$StopLoop = $true
Exit
}
else {
$Retrycount = $Retrycount + 1
Write-Host ‘Invalid author name. Try again. Name must be between 1-40 characters’ -ForegroundColor Yellow
}
}
} While ($StopLoop -eq $false)

Your first parameter code should look something like this, which would validate the input.

[Parameter()]
[ValidatePattern('^[a-zA-Z0-9\-]{1,40}$')]
[string]$ProjectName
This would not have the repeating retry loop, it would simply give an error and not run the function if the parameter doesn't match the validation.

Thanks for the input @Darwin-Reiswig but I want to keep the way the script works with the more user friendly warning/error messages, the retry loop and add the possibility to pass the parameters inline.

While waiting for this post to be released from the spam/moderation filter I’ve ran some more tests and the code below is what I came up with. It’s almost there but there are some nagging issues left to work out.

  • Putting the parameters at the start of the script obviously causes you to enter both before getting any information about the first one not matching the RegEx. I don't know if there's any way to avoid or work around this except for putting the validation within the parameter itself as Darwin suggested.
  • Moving the parameters inside the functions (I've commented out the code below) solves the above but then you lose the ability to pass the parameters inline with the script.
    • Another side effect of this is that the variables are stuck within the functions so the Write-Host at the end has no values to output. I'm guessing this is a scope issue but haven't been able to figure exactly what object I should change the scope on.
Param (
    $ProjectName = $(
        $EnterProjectName = Write-Host 'Enter the name of the project: ' -ForegroundColor Green -NoNewline
        Read-Host
        ),
    $AuthorName = $(
        $EnterAuthorName = Write-Host 'Enter the name of the author: ' -ForegroundColor Green -NoNewline
        Read-Host
        )
)

Function ValidateProjectName {
<#Param (
$ProjectName = $(
Write-Host 'Enter the name of the project: ’ -ForegroundColor Green -NoNewline
Read-Host
)
)#>
$RegEx = ‘^[a-zA-Z0-9-]{1,40}$’
$StopLoop = $false
[int]$Retrycount = “0”
Do{
If ($ProjectName -match $RegEx) {
$StopLoop = $true
}
ElseIf ($ProjectName -notmatch $RegEx -and $Retrycount -gt 1) {
Write-Host ‘Too many retries. Exiting’ -ForegroundColor Red
$StopLoop = $true
Exit
}
Else {
$Retrycount = $Retrycount + 1
Write-Host ‘Invalid project name. Try again. Name must be between 1-40 characters and only allows these characters: a-z, A-Z, 0-9 and -’ -ForegroundColor Yellow
Write-Host 'Enter the name of the project: ’ -ForegroundColor Green -NoNewline
$ProjectName = Read-Host
}

} While ($StopLoop -eq $false)

}

Function ValidateAuthorName {
<#Param (
$AuthorName = $(
Write-Host 'Enter the name of the author: ’ -ForegroundColor Green -NoNewline
Read-Host
)
)#>
$RegEx = ‘^.{1,40}$’
$StopLoop = $false
[int]$Retrycount = “0”
Do{
If ($AuthorName -match $RegEx) {
$StopLoop = $true
}
ElseIf ($AuthorName -notmatch $RegEx -and $Retrycount -gt 1) {
Write-Host ‘Too many retries. Exiting’ -ForegroundColor Red
$StopLoop = $true
Exit
}
Else {
$Retrycount = $Retrycount + 1
Write-Host ‘Invalid author name. Try again. Name must be between 1-40 characters’ -ForegroundColor Yellow
Write-Host 'Enter the name of the author: ’ -ForegroundColor Green -NoNewline
$AuthorName = Read-Host
}

} While ($StopLoop -eq $false)

}

ValidateProjectName
ValidateAuthorName
Write-Host “Project name:$ProjectName, Author name: $AuthorName”


 

I would break the logic up some. Also try to avoid repeating the same code. Based on my testing, it seems to accomplish your desired goals.

Param (
    $ProjectName,
    $AuthorName
)

Function Get-ProjectName {
    Param
    (
        $Project = $(
            Write-Host 'Enter the name of the project: ' -ForegroundColor Green -NoNewline
            Read-Host
        )
    )

    ValidateProjectName $Project
}

Function ValidateProjectName {
    Param
    (
        $Project = $(
            Write-Host 'Enter the name of the project: ' -ForegroundColor Green -NoNewline
            Read-Host
        )
    )
    $RegEx = '^[a-zA-Z0-9\-]{1,40}$'

    If ($Project -match $RegEx)
    {
        $Project
    }
    Else
    {
        Write-Host 'Invalid project name. Try again. Name must be between 1-40 characters and only allows these characters: a-z, A-Z, 0-9 and -' -ForegroundColor Yellow
    }
}

Function Get-AuthorName {
    Param
    (
        $Author = $(
            Write-Host 'Enter the name of the author: ' -ForegroundColor Green -NoNewline
            Read-Host
        )
    )

    ValidateAuthorName $Author

}

Function ValidateAuthorName {
    Param
    (
        $Author
    )

    $RegEx = '^.{1,40}$'

    If ($Author -match $RegEx)
    {
        $Author
    }
    Else
    {
        Write-Host 'Invalid author name. Try again. Name must be between 1-40 characters' -ForegroundColor Yellow
    }
}

$retrycount = 0

do
{
    if($AuthorName){$ValidAuthor = ValidateAuthorName $AuthorName}
    if(!$ValidAuthor){$ValidAuthor = Get-AuthorName}
    if(!$ValidAuthor){$retrycount++}
}
until($ValidAuthor -or $retrycount -eq 3)

do
{
    if($ProjectName){$ValidProject = ValidateProjectName $ProjectName}
    if(!$ValidProject){$ValidProject = Get-ProjectName}
    if(!$ValidProject){$retrycount++}
}
until($ValidProject -or $retrycount -eq 3)

if($retrycount -eq 3)
{
    Write-Host 'Too many retries. Exiting' -ForegroundColor Red
    break
}

Write-Host "Project name: $ValidProject, Author name: $ValidAuthor"

@KrzyDoug thanks for the suggestion and for running a DRY pass at the same time. I’m aware of the repeating code in my scripts and am working towards minimizing that part.

I’ve ran some tests on both my latest attempt and on @KrzyDoug suggestion and found some issues summarized below. I will troubleshoot this best I can but I suspect I’ll need some help with that. Primary issues are that in my script Write-Host outputs the wrong names for the variables if you enter valid parameters on 2:nd or 3:rd attempt and in @KrzyDoug’s suggestion the warning message duplicates and it gets stuck in a loop in certain instances. I saved transcripts from both testing sessions and they’re available here.

My attempt

Run script - Valid $ProjectName and $AuthorName - Works. - Valid $ProjectName and invalid $AuthorName - Works. - Invalid $ProjectName and valid $AuthorName - Works but confusing when it doesn't trigger the warning until both parameters have been entered. - Invalid $ProjectName and invalid $AuthorName - Works but confusing when it doesn't trigger the warning until both parameters have been entered. - Invalid $ProjectName and invalid $AuthorName and enter valid parameters on 2:nd or 3:rd attempt - Write-Host outputs the wrong names for the variables.

Inline parameters

  • Valid $ProjectName and $AuthorName - Works.
  • Valid $ProjectName and invalid $AuthorName - Works.
  • Invalid $ProjectName and valid $AuthorName - Works.
  • Invalid $ProjectName and invalid $AuthorName - Works.
  • Invalid $ProjectName and invalid $AuthorName and enter valid parameters on 2:nd or 3:rd attempt - Write-Host outputs the wrong names for the variables.

@KrzyDoug suggestion

Run script - Valid $ProjectName and $AuthorName - Works. - Valid $ProjectName and invalid $AuthorName - Almost works. Displays warning message above final error message. - Invalid $ProjectName and valid $AuthorName - Doesn't stop the script after third ProjectName attempt and displays error message only after entering a valid AuthorName. - Invalid $ProjectName and invalid $AuthorName - Doesn't stop the script after third ProjectName attempt and gets stuck in endless loop on AuthorName. Entering a valid AuthorName exits the loop gracefully.

Inline parameters

  • Valid $ProjectName and $AuthorName - Works.
  • Valid $ProjectName and invalid $AuthorName - Almost works. Displays warning message twice but exits after third attempt.
  • Invalid $ProjectName and valid $AuthorName - Almost works. Displays warning message twice but exits after third attempt.
  • Invalid $ProjectName and invalid $AuthorName - Displays warning message twice and doesn’t stop after third ProjectName attempt and gets stuck in endless loop on AuthorName. Entering a valid AuthorName exits the loop gracefully.