Hi all, new to pulumi. Im trying to use multiple p...
# getting-started
b
Hi all, new to pulumi. Im trying to use multiple projects (trying to implement micro stacks) in azure. I have a project that creates a vnet and has an output of the vnet name, my second project then takes this output and creates a subnet on it. Obviously I have to run pulumi up in the right order for this to work and I’m happy that the Stsckrefence will throw a decent exception if I forget to do this, however if I accidentally destroy in the wrong order there doesn’t seem to be any warning or anything and my resources can be destroyed in the wrong order causing my state to break. Specifically the vnet shows as being deleted in pulumi but its still in my portal but my subnets are gone. Am I missing something with this? Secondly if I run my second project without running my first is there anyway of telling the stack I depend on to go ahead and run if it’s outputs don’t exist?
b
Hi Mark, I orchestrate with PowerShell so stacks get updated and destroyed in order. If a change is made through the portal you will need to run "pulumi refresh" to resolve differences.
Copy code
function run-infra{
    Param(
        [Alias("a")]
        [parameter(Mandatory = $true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$True, HelpMessage="Pulumi action to run.")]
        [ValidateSet("up","destroy","refresh")]
        [string] $action,

        [Alias("e")]
        [parameter(Mandatory = $true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$True, HelpMessage="Environment variable used in resource names. Default value = dev")]
        [string] $env,

        [Alias("t")]
        [parameter(Mandatory = $true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$True, HelpMessage="Path to local transcripts folder.")]
        [string] $transcriptsDir,

        [Alias("p")]
        [parameter(Mandatory = $true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$True, HelpMessage="Path to local folder that contains Pulumi projects.")]
        [string] $projectsRoot,

        [Alias("s")]
        [parameter(Mandatory = $false, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$True, HelpMessage="Steps to skip as. Example 1,3,4,5 only applies step 2")]
        [string[]] $skip = @()
    )


$null = New-Item -Path $transcriptsDir  -ItemType Directory -ErrorAction Ignore
Start-Transcript -Path "$transcriptsDir\$(((get-date).ToUniversalTime()).ToString("yyyyMMddTHHmmssZ")))_Transcript.txt"

Get-Date 

function Deploy {
    param (
        $stack
    )
    switch($stack)
    {
        1 {
            write-host "az_hub ($($env)Test10) > pulumi $action "
            set-location "$projectsRoot\az_hub"
            if ($env -ieq "dev") {
                pulumi stack select devTest10
                pulumi $action -y -f
                pulumi stack select devTest172
                pulumi $action -y -f
            }elseif ($env -ieq "sys") {
                pulumi stack select sysTest10
                pulumi $action -y
                pulumi stack select sysTest172
                pulumi $action -y
            }
            Break
        } 2 {
            write-host "az_hub_panfw ($($env)Test10) > pulumi $action "
            # Depends On: az_hub-devTest10
            if ($action -ieq "up"){
            az vm image accept-terms --urn "paloaltonetworks:vmseries-flex:byol:10.2.3"
            }

            set-location "$projectsRoot\az_hub_panfw"
            if ($env -ieq "dev") {
                pulumi stack select devTest10
                pulumi $action -y -f
                pulumi stack select devTest172
                pulumi $action -y -f
            }elseif ($env -ieq "sys") {
                pulumi stack select sysTest10
                pulumi $action -y
                pulumi stack select sysTest172
                pulumi $action -y
            }
            Break
        } 3 {
            write-host "az_panfw_lb ($($env)Test10) > pulumi $action "
            # Depends On: az_hub-devTest10
            set-location "$projectsRoot\az_panfw_lb"
            if ($env -ieq "dev") {
                pulumi stack select devTest10
                pulumi $action -y -f
                pulumi stack select devTest172
                pulumi $action -y -f
            }elseif ($env -ieq "sys") {
                pulumi stack select sysTest10
                pulumi $action -y
                pulumi stack select sysTest172
                pulumi $action -y
            }
            Break
        } 4 {
            write-host "az_hub_spoke (pano-devTest) > pulumi $action "
            # Depends On: az_hub-devTest10
            Set-Location "$projectsRoot\az_hub_spoke"
            if ($env -ieq "dev") {
                pulumi stack select pano-devTest
                pulumi $action -y -f
            }elseif ($env -ieq "sys") {
                pulumi stack select pano-sysTest
                pulumi $action -y
            }
            Break
        } 5 {
            write-host "az_spoke_panorama (devTest) > pulumi $action "
            # Depends On: az_hub_spoke-pano-devTest
            set-location "$projectsRoot\az_spoke_panorama"
            if ($env -ieq "dev") {
                pulumi stack select devTest
                pulumi $action -y -f
            }elseif ($env -ieq "sys") {
                pulumi stack select devTest
                pulumi $action -y
            }
        }
    }
}

if ( ($action -ieq "up") -or ($action -ieq "refresh") )
{
    1..5 | % { if($skip.Contains($_) -eq $false){ Deploy -stack $_} }
}elseif ( $action -ieq "destroy" ) {
    5..1 | % { if($skip.Contains($_) -eq $false) { Deploy -stack $_ }}
}

Get-Date 
Stop-Transcript

}

# Run-Infra -action "up" -env "dev" -t 'D:\_Repos\Infrastructure\Transcripts' -projectsRoot 'D:\_Repos\Infrastructure'
The script above and related projects create a hub and spoke network and use stack references to build dependencies.
b
Thank you for the response, I wish there was a native way to protect against doing this accidentally but a script to run them will also work.
b
I wrote logic that checks target subscription and stack reference. Basically in every stack I define the target subscription because I would hate to update production with a dev config.
Copy code
config:
  cfg:stackRefOrg: dmanson
  cfg:stackRefProject: az_hub
  cfg:stackRefEnv: devTest10
  cfg:targetSubscriptionId: 44444444-0000-0000-0000-111111111111
Then in the stack something like this is called. You can prevent the stacks from running out of sequence easily. Your logic would siply need to know its dependencies, its stack Ref(s), but also everything that depends on it for destroy, not as easy.
Copy code
_hubStack = new StackReference($"{_config.Require("stackRefOrg")}/{_config.Require("stackRefProject")}/{_config.Require("stackRefEnv")}");
        this.StackRef = _hubStack.Name;

        var currentConfig = (Pulumi.AzureNative.Authorization.GetClientConfigResult) Pulumi.AzureNative.Authorization.GetClientConfig.InvokeAsync().GetAwaiter().GetResult();
        var targetSubscriptionId = _config.Require("targetSubscriptionId");
        if(currentConfig.SubscriptionId != targetSubscriptionId)
        {
            Pulumi.Log.Error($"The target subscription {targetSubscriptionId} is not the current subscription. Use `az account set --subscription {targetSubscriptionId}` to select the expected subscription");
        }
        this.PanFwSubscriptionId = Output.Create(currentConfig.SubscriptionId);
Something to think about.
b
I suppose over time that the destroy becomes less important because im unlikely ever to want to destroy shared infrastructure (at least not all in one go), only individual apps that will be in their own stacks. Im using destroy quite heavily right now just while testing things out. StackReference with RequireOutput(C#) has been great for catching out running updates/creations in the wrong order.