2
votes

I added a menu portion of my script which is using a function to set-variables for connections to vcenter servers. The problem is it isn't passing the variable. When I quit the script the variable is correct when I do a get-variable.

#Main menu
function MainMenu {
    Clear-Host
    [int]$xMenuChoiceA = 0

    while ($xMenuChoiceA -lt 01 -or $xMenuChoiceA -gt 04) {
        Write-Host "============================================" -ForegroundColor Green
        Write-Host "|   Main Menu                              |" -ForegroundColor Green
        Write-Host "============================================" -ForegroundColor Green
        Write-Host "|  Migration Script                        |" -ForegroundColor Green
        Write-Host "============================================" -ForegroundColor Green
        Write-Host "|                                          |" -ForegroundColor Green
        Write-Host "| 1. Development                           |" -ForegroundColor Green
        Write-Host "| 2. Model                                 |" -ForegroundColor Green
        Write-Host "| 3. Production                            |" -ForegroundColor Green
        Write-Host "| 4. Quit                                  |" -ForegroundColor Green
        Write-Host "|                                          |" -ForegroundColor Green
        Write-Host "============================================" -ForegroundColor Green
        Write-Host ""
        Write-Host "Please enter an option 1 to 4..." -ForegroundColor Green -NoNewline

        [Int]$xMenuChoiceA = Read-Host
    }

    Switch($xMenuChoiceA) {
        1 { SetDev }
        2 { SetModel }
        3 { SetProd }
        4 { Are-YouSure }
        default { MainMenu }
    }
}

#Verifiyng exit of menu
function Are-YouSure {
    Write-Host "Are you sure you want to exit? (y/n)" -ForegroundColor Cyan -NoNewline
    $areyousure = Read-Host

    if ($areyousure -eq "y") { exit }
    if ($areyousure -eq "n") { MainMenu }
    else {
        Write-Host -ForegroundColor Red "Invalid Selection"
        Are-YouSure
    }
}

#clear vCenter variables
$SourceVC = $null
$DestVC = $null

#menu set vCenter function
function SetDev {
    Set-Variable -Name SourceVC -Value devvc.effingps.corp -Scope global
    Set-Variable -Name DestVC -Value ucsvcd.effingps.corp -Scope global
    break
}

function SetModel {
    Set-Variable -Name SourceVC -Value modelvc.effingps.corp -Scope global
    Set-Variable -Name DestVC -Value ucsvcm.effingps.corp -Scope global
    break
}

function SetProd {
    Set-Variable -Name SourceVC -Value prodvc.effingps.corp -Scope global
    Set-Variable -Name DestVC -Value ucsvcp.effingps.corp -Scope global
    break
}

MainMenu

#connects to the source vCenter for right-sizing
Connect-VIServer $SourceVC

When it fails it looks like the variable is not passed and it's using the literal $variable instead of the variable that was set in the function. Get-variable returns the correct variable (as does write-host $sourceVC, etc). Here's the error:

Connect-VIServer : Cannot validate argument on parameter 'Server'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.

At D:\scripts\vm-uscmigration.ps1:84 char:18

+ connect-viserver $SourceVC + ~~~~~~~~~ CategoryInfo : InvalidData: (:) [Connect-VIServer], ParameterBindingValidationException + FullyQualifiedErrorId : ParameterArgumentValidationError,VMware.VimAutomation.ViCore.Cmdlets.Commands.ConnectVIServer

The script did work fine with manual input for the servers, but as Operations will be running the script, I wanted to take manual error out of the equation since the source and destination vCenters are known.

Thanks!

2
$SourceVC = $null if that done not in global scope, then this variable have priority over variable with the same name in global scope.user4003407
When in ISE this works, so I assume running this in console by calling a script file changes the global scope ?sodawillow
I've tried removing the Scope to no avail...same error.The Slayer
@sodawillow When you run script in ISE by hitting F5 it does not create new scope for script, but run script in global scope.user4003407
@The Slayer : I believe if you remove the scope you only change the variable in function scope. See PetSerAl's answersodawillow

2 Answers

1
votes

I suggest to use ([ref]$Variable).Value='NewValue' to update variable in the scope where it defined.

function SetDev {
    ([ref]$SourceVC).Value='devvc.effingps.corp'
    ([ref]$DestVC).Value='ucsvcd.effingps.corp'
    break
}
function SetModel {
    ([ref]$SourceVC).Value='modelvc.effingps.corp'
    ([ref]$DestVC).Value='ucsvcm.effingps.corp'
    break
}
function SetProd {
    ([ref]$SourceVC).Value='prodvc.effingps.corp'
    ([ref]$DestVC).Value='ucsvcp.effingps.corp'
    break
}
0
votes

When this script is run from within the ISE by pressing F5, the ISE will actually run the scripts contents in the global scope just as if you were to copy and paste the code in the console. This will:

  • Clear the vCenter variables in the global scope.
  • The functions will update vCenter variables in the global scope
  • Connect-VIServer will use the $SourceVC variable in the global scope

On the other hand, if the script is run from the console by using an absolute or relative path, then the code will run within it's own scope. This scope is known as the script scope and this will:

  • Clear the vCenter variables in the script scope
  • The functions will update the vCenter variables in the global scope
  • Connect-VIServer will use the $SourceVC variable in the script which is still set to $null.

There are two ways around this:

  1. Use so-called "dot-sourcing" to run the content of your script in the global scope in the console. This is done by adding a dot(period) before the path of the script. For example:

. .\myscript.ps1

  1. Either have the functions modify the variables in the script scope by changing the scope parameter of Set-Variable from -Scope Global to -Scope Script, or access the $SourceVC variable in the global scope by adding a scope modifier like this:

Connect-VIServer $Global:SourceVC

You can read more about scopes in the help document about_Scopes.