Search code examples
powershellazure-service-fabric

Invoke PowerShell from cmd, context gets lost when that PowerShell calls into another module


I have a PowerShell script which calls into ServiceFabricSDK PowerShell module to trigger Azure Service Fabric Application Deployment. All works fine when I call that PowerShell directly, but if I call it from a cmd file using powershell command, things get messed up and it looks like some context gets lost between my PowerShell script and the module it calls into.

Here's what I mean:

My PowerShell script takes application name and couple more parameters, imports ServiceFabricSDK.psm1, runs Connect-ServiceFabricCluster to connect to local Service Fabric cluster and ultimately invokes Publish-NewServiceFabricApplication to start the deployment:

param (
    [Parameter()]
    [ValidateNotNullOrEmpty()]
    [string]
    $applicationName,

    [Parameter()]
    [ValidateNotNullOrEmpty()]
    [string]
    $repoTargetPath,

    [Switch]
    $retail,

    [Parameter()]
    [ValidateNotNullOrEmpty()]
    $targetEnvironment = "dev.local"
)

# Load ServiceFabricSDK first
Write-Output "Loading Service Fabric SDK module"
Import-Module "$ENV:ProgramFiles\Microsoft SDKs\Service Fabric\Tools\PSModule\ServiceFabricSDK\ServiceFabricSDK.psm1"

# Connect to the local cluster
Write-Output "Connecting to local Service Fabric cluster"
Connect-ServiceFabricCluster | Out-Null
Write-Output "Successfully connected to local Service Fabric cluster"

$repoTargetPath = $repoTargetPath.TrimEnd('\')
$target = if($retail.IsPresent) { "retail" } Else { "debug" }
$applicationTargetBasePath = "$repoTargetPath\distrib\$target\applications\$applicationName"
$applicationPackagePath = "$applicationTargetBasePath\package\"
$applicationParametersFilePath = "$applicationTargetBasePath\applicationParameters\$applicationName.$targetEnvironment.xml"

Write-Output "Deploying application $applicationName to local Service Fabric cluster"

# Deploy the application
Publish-NewServiceFabricApplication -ApplicationPackagePath $applicationPackagePath -ApplicationParameterFilePath $applicationParametersFilePath -ApplicationName "fabric:/$applicationName" -OverwriteBehavior Always -Verbose -SkipPackageValidation

If I run it directly it all works fine:

PS G:\repo> .\build\scripts\Deploy-Application.ps1 -applicationName HelloWorld -targetEnvironment "dev.local" -repoTargetPath G:\repo\target\
Loading Service Fabric SDK module
Connecting to local Service Fabric cluster
WARNING: Cluster connection with the same name already existed, the old connection will be deleted
Successfully connected to local Service Fabric cluster
Validating the package and application parameters
Deploying application HelloWorld to local Service Fabric cluster
An application with name 'fabric:/HelloWorld' already exists in the cluster with Application Type 'HelloWorldType' and Version '1.0.20170816-seed-1    207928760'. Removing it.
Remove application instance succeeded
Unregister application type succeeded.
Copying application to image store...
Copy application package succeeded
Registering application type...
Register application type succeeded
Removing application package from image store...
Remove application package succeeded
Creating application...

ApplicationName        : fabric:/HelloWorld
ApplicationTypeName    : HelloWorldType
ApplicationTypeVersion : 1.0.20170816-seed-1207928760
ApplicationParameters  : { "HelloWorldServiceType_InstanceCount" = "2" }

Create application succeeded.

I also have appStart.cmd file which calls into that PowerShell:

@if "%_echo%"=="" echo off

pushd
cd %BaseDir%

if "%1" == "" goto error

echo powershell %ScriptsPath%\Deploy-Application.ps1 -applicationName "%1" -repoTargetPath "%BaseDir%\\target\\" -targetEnvironment "dev.local"
call powershell %ScriptsPath%\\Deploy-Application.ps1 -applicationName "%1" -repoTargetPath "%BaseDir%\\target\\" -targetEnvironment "dev.local"

GOTO :EOF

There is nothing more than just calling into that PowerShell, but unfortunately the deployment fails, because the module sees no cluster connection:

G:\repo>appStart HelloWorld
powershell G:\repo\build\scripts\Deploy-Application.ps1 -applicationName "HelloWorld" -repoTargetPath "G:\repo\\target\\" -targetEnvironment "dev.local"
Loading Service Fabric SDK module
Connecting to local Service Fabric cluster
Successfully connected to local Service Fabric cluster
Validating the package and application parameters
Deploying application HelloWorld to local Service Fabric cluster
VERBOSE: System.NullReferenceException: Cluster connection instance is null
WARNING: Unable to Verify connection to Service Fabric cluster.
Get-ServiceFabricClusterConnection : Cluster connection instance is null
At C:\Program Files\Microsoft SDKs\Service
Fabric\Tools\PSModule\ServiceFabricSDK\Publish-NewServiceFabricApplication.ps1:144 char:1
+ Get-ServiceFabricClusterConnection
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ResourceUnavailable: (:) [Get-ServiceFabricClusterConnection], NullReferenceException
    + FullyQualifiedErrorId : GetClusterConnectionErrorId,Microsoft.ServiceFabric.Powershell.GetClusterConnection

I played with it and Connect-ServiceFabricCluster result can be checked by Get-ServiceFabricClusterConnection, which is what Publish-NewServiceFabricApplication.ps1 is doing. I can run it in my PowerShell right before calling into Publish-NewServiceFabricApplication.ps1 and it returns the right connection data, even when run from appStart.cmd. Why is the connection lost when called into the script?


Solution

  • You can try putting the cluster connection into a global variable after Connect-ServiceFabricCluster:

    $global:clusterConnection = $clusterConnection  
    

    More info here How do I deploy service fabric application from VSTS release pipeline?