Function not working when called from within switch

Long story short; our users each get an “S:” drive which acts as their home network share. My problem, these folders are never cleaned up after the employee leaves. I need a way to automate the migration of these dead directories to an archive location where they will be held until deletion.

I’ve wrote a script (I’m a newb) that should help with this. It has it’s own little menu which I really like because it would be nice for the person who will be running this. The first part of this script gets all dir names that match my regex. It then checks to see if the dir names appear in AD. Each dir name is then placed in a System.Object based on the result of the AD lookup. I have 3 of these:
$LinkGoodCollection = Employee acct still exists in AD
$LinkOKCollection = Employee acct is disabled
$LinkBadCollection = No acct found (the directory name could not be matched to any AD samaccountname)

So If I run this and choose option 2 or 3 it will migrate everything just fine. The problem is that after migrating the data my System.Object variables need to be updated. Because if someone chooses option 1 (the report option) it will still show the contents of the $LinkGoodCollection, $LinkOKCollection & $LinkBadCollection PRIOR to preforming the migrate. So it reports old data. I tried to create a function “Refresh-Data” that should go out and re-run the dir sorting process in the the Good, OK & Bad variables. But it doesn’t work. I get no errors or anything to say that the “Refresh-Data” function failed.

Thanks in advance for any help!

#BEGIN ENV VARIABLES------>
                $OU = 'TBD'
                $regex = '^[a-zA-Z]{7}$'
                $path = 'C:\Users\TEST\Desktop\Test\'
                $ArchiveLocation = '\\archive\archive\S-Drive Archive'

                $LinkGoodCollection = New-Object System.Collections.Generic.List[System.Object]
                $LinkOKCollection = New-Object System.Collections.Generic.List[System.Object]
                $LinkBadCollection = New-Object System.Collections.Generic.List[System.Object]
                     
                

                $SdriveFolders = Get-ChildItem $path `
                -Directory | 
                where name -match $regex | 
                select -ExpandProperty name
#
function Show-Menu
{
     param (
           [string]$Title = 'S DRIVE CONSISTENCY CHECK'
     )
     cls
     Write-Host "================ $Title ================"
     
     Write-Host "1: SHOW S: DRIVE USER REPORT"
     Write-Host "2: MIGRATE DISABLED USERS S: DRIVE"
     Write-Host "3: MIGRATE DELETED USERS S: DRIVE"
     Write-Host "4: SETTINGS"
     Write-Host "Q: Press 'Q' to quit…"
}

Function Show-Settings
{
    param (
           [string]$SettingsTitle = 'SETTINGS'
     )
     cls
     Write-Host "================ $SettingsTitle ================"
     
     Write-Host "1: CHANGE OU SCOPE: " -NoNewline 
     Write-Host "($OU)"
     Write-Host "2: CHANGE REGEX MATCHING PATTERN: " -NoNewline 
     Write-Host "($REGEX)"
     Write-Host "3: CHANGE ARCHIVE LOCATION: " -NoNewline 
     Write-Host "($ArchiveLocation)"
     Write-Host "Q: Press 'Q' to return to main menu…"
}

Function Refresh-Data
{
    Clear-Variable LinkGoodCollection -Force
    Clear-Variable LinkOKCollection -Force
    Clear-Variable LinkBadCollection -Force
    Clear-Variable DirName -Force
    Clear-Variable SdriveFolders -Force

    $LinkGoodCollection = New-Object System.Collections.Generic.List[System.Object]
    $LinkOKCollection = New-Object System.Collections.Generic.List[System.Object]
    $LinkBadCollection = New-Object System.Collections.Generic.List[System.Object]

    $SdriveFolders = Get-ChildItem $path `
                -Directory | 
                where name -match $regex | 
                select -ExpandProperty name

    $i = foreach ($DirName in $SdriveFolders){
                    $AcctFound = Get-ADUser -LDAPFilter "(samaccountname=$DirName)" -ErrorAction SilentlyContinue
    
                        if ($AcctFound.Enabled -eq $true){$LinkGoodCollection.Add($DirName)}
                        
                        if ($AcctFound.Enabled -eq $false){$LinkOKCollection.Add($DirName)}

                        if ($AcctFound -eq $null){$LinkBadCollection.Add($DirName)}
                    }    
}

function Pause-Script
{
   Read-Host 'Press enter to continue…' | Out-Null
}
#

            $i = foreach ($DirName in $SdriveFolders){
                    $AcctFound = Get-ADUser -LDAPFilter "(samaccountname=$DirName)" -ErrorAction SilentlyContinue
    
                        if ($AcctFound.Enabled -eq $true){$LinkGoodCollection.Add($DirName)}
                        
                        if ($AcctFound.Enabled -eq $false){$LinkOKCollection.Add($DirName)}

                        if ($AcctFound -eq $null){$LinkBadCollection.Add($DirName)}
                    }

# 
do
{
     Show-Menu
     $input = Read-Host "Please make a selection"
     switch ($input)
     {
           '1' {
                 cls
                    $i = foreach ($DirName in $SdriveFolders){
                    $AcctFound = Get-ADUser -LDAPFilter "(samaccountname=$DirName)" -ErrorAction SilentlyContinue
    
                        if ($AcctFound.Enabled -eq $true){
                            $FullName = Get-ADUser -Identity $DirName | select -ExpandProperty name -ErrorAction SilentlyContinue
                            Write-Host "$path$DirName : IS LINKED TO AN AD USER ACCT (COMPANY\$FullName)" -ForegroundColor Green
                        }

                        if ($AcctFound.Enabled -eq $false){
                            $FullName = Get-ADUser -Identity $DirName | select -ExpandProperty name -ErrorAction SilentlyContinue
                            write-host "$path$DirName : USER IS DISABLED BUT STILL HAS AN S DRIVE (COMPANY\$FullName)" -ForegroundColor Yellow
                        }

                        if ($AcctFound -eq $null){
                            write-host "$path$DirName : NO USER ACCOUNT FOUND FOR THIS DIRECTORY" -ForegroundColor Red
                        }
                    }
                Write-host "Number of S:\ drives without an owner: " ($LinkBadCollection.Count)
                Write-host "Number of S:\ drives owned by disabled AD users: " ($LinkOKCollection.Count)
                Write-host "Number of S:\ drives owned by active AD users: " ($LinkGoodCollection.Count)
    
                Pause-Script
            }

            '2' {
                  cls

                    Write-Host "Checking archive status '$ArchiveLocation'"

                    if((test-path $ArchiveLocation) -eq $true){Write-Host "Path is good…" -ForegroundColor Green}
                    
                    if((test-path $ArchiveLocation) -eq $false){Write-Host "Default path ($ArchiveLocation) is not accessible." -ForegroundColor Red
                        Do {$ArchiveLocation = Read-Host -Prompt "Please supply a new archive location…"}
                            Until ((Test-Path $ArchiveLocation) -eq $true)}

                        foreach ($DirName in $LinkOKCollection){
                            write-host "Migrating disabled user ($DirName's) S: drive to the archive location…" -ForegroundColor Green
                            New-Item -Path $ArchiveLocation -name $DirName -ItemType "directory"
                            robocopy.exe "$Path$DirName" "$ArchiveLocation\$DirName" /log+:"$ArchiveLocation\Robocopy Logs\$DirName.txt" --% /e /copy:datou /move /r:1 /w:2 /np | Out-Null
                        }
                        Write-Host "Migration completed successfully" -ForegroundColor Green
                        Refresh-Data
                        Pause-Script
            }

            '3'{
                 cls

                    Write-Host "Checking archive status '$ArchiveLocation'"

                    if((test-path $ArchiveLocation) -eq $true){Write-Host "Path is good…" -ForegroundColor Green}
                    
                    if((test-path $ArchiveLocation) -eq $false){Write-Host "Default path ($ArchiveLocation) is not accessible." -ForegroundColor Red
                        Do {$ArchiveLocation = Read-Host -Prompt "Please supply a new archive location…"} 
                            Until ((Test-Path $ArchiveLocation) -eq $true)}

                        foreach ($DirName in $LinkBadCollection){
                            write-host "Migrating disabled user ($DirName's) S: drive to the archive location…" -ForegroundColor Green
                            New-Item -Path $ArchiveLocation -name $DirName -ItemType "directory"
                            robocopy.exe "$Path$DirName" "$ArchiveLocation\$DirName" /log+:"$ArchiveLocation\Robocopy Logs\$DirName.txt" --% /e /copy:datou /move /r:1 /w:2 /np | Out-Null
                        }
                        Write-Host "Migration completed successfully" -ForegroundColor Green
                        Refresh-Data
                        Pause-Script
            
            }

            '4' {
                  cls

                  do{
                    Show-Settings
                    $SettingInput = Read-Host "Please make a selection"
                    switch ($SettingInput)

                    {
                    '1'
                        {$OU = Read-Host "Please enter a new OU: "
                        Show-Settings
                        }

                    '2' {$regex = Read-Host "Please enter a new REGEX: "
                        Show-Settings
                        }

                    '3' {$ArchiveLocation = Read-Host "Please enter a new archive location: "
                        Show-Settings
                        }
                    }
                    }until ($SettingInput -eq 'q' )
            }
    }
}until ($input -eq 'q')
#<------END SWITCHING

Hey Jared,
Your issue is likely because of your scope. When you set your variables inside of the function that are only relevant to the function, not to the rest of the script. For more information about scopes see about_scopes (https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.core/about/about_scopes)

For Example

Remove-Variable test -ErrorAction SilentlyContinue
$test = 123

Function testfun {
    Write-Host "Inside Before: $test"
    $test = 234
    Write-Host "Inside After: $test"
}
Write-Host "Before: $test"
Testfun
Write-Host "After: $test"

Results:

Before: 123
Inside Before: 123
Inside After: 234
After: 123

As you can see, when I set the value of $test inside of the function, it maintained the value set inside of the function; however, after the function ended, the $test variable was still it’s original value.

To facilitate what you are trying to do, you can change the scope of your variable when you set it, so that it works with the variable outside of it’s normal scope.

For Example:

Remove-Variable test -ErrorAction SilentlyContinue
$test = 123

Function testfun {
    Write-Host "Inside Before: $test"
    $script:test = 234
    Write-Host "Inside After: $test"
}
Write-Host "Before: $test"
Testfun
Write-Host "After: $test"

Results:

Before: 123
Inside Before: 123
Inside After: 234
After: 234

Since I instructed the script to use the variable in the scope of the script, the variable maintained the new value even after the function ended.

This should help you accomplish what you are trying to do; however, I personally do not believe using variable scopes like this to be in good practice as it makes your functions much harder to reuse in other scripts. You should be able to write your functions where you provide information to and get results from the function. The function should not write values to variables outside of its own scope.

For Example: I’d be more inclined to do something like this

Remove-Variable test -ErrorAction SilentlyContinue
$variables = @{
    test1 = 123;
    test2 = 123;
    test3 = 123
}

Function updateVars {
    @{
        test1 = 234;
        test2 = 345;
        test3 = 456
    }
}
Write-Host "Before: $($variables['test1']), $($variables['test2']), $($variables['test3'])"
$variables = updateVars -value1 $test1 -value2 ([ref]$test2) -value3 ([ref]$test3)
Write-Host "After: $($variables['test1']), $($variables['test2']), $($variables['test3'])"

Results:

Before: 123, 123, 123
After: 234, 345, 456

Or maybe use a pscustomobject rather than a hash table

Hope that helps