Azure - Deploy Container App

Octopus.Script exported 2023-07-05 by twerthi belongs to ‘Azure’ category.

Deploys a container to an Azure Container App Environment

Parameters

When steps based on the template are included in a project’s deployment process, the parameters below can be set.

Azure Resource Group Name

Template.Azure.ResourceGroup.Name =

Provide the resource group name to create the environment in.

Azure Account Subscription Id

Template.Azure.Account.SubscriptionId =

The subscription ID of the Azure account to use. This value can be retrieved from an Azure Account variable type. Add an Azure Account to your project , then assign the SubscriptionNumber property to for this entry. Leave blank to use the Managed Identity.

For example, if your Azure Account variable is called MyAccount, the value for this input would be #{MyAccount.SubscriptionNumber}

Azure Account Client Id

Template.Azure.Account.ClientId =

The subscription ID of the Azure account to use. This value can be retrieved from an Azure Account variable type. Add an Azure Account to your project , then assign the Client property to for this entry. Leave blank to use the Managed Identity.

For example, if your Azure Account variable is called MyAccount, the value for this input would be #{MyAccount.Client}

Azure Account Tenant Id

Template.Azure.Account.TenantId =

The subscription ID of the Azure account to use. This value can be retrieved from an Azure Account variable type. Add an Azure Account to your project , then assign the TenantId property to for this entry. If blank, it will use the Managed Identity tenant.

For example, if your Azure Account variable is called MyAccount, the value for this input would be #{MyAccount.TenantId}

Azure Account Password

Template.Azure.Account.Password =

The subscription ID of the Azure account to use. This value can be retrieved from an Azure Account variable type. Add an Azure Account to your project , then assign the Password property to for this entry. Leave blank to use the Managed Identity.

For example, if your Azure Account variable is called MyAccount, the value for this input would be #{MyAccount.Password}

Container App Environment Name

Template.ContainerApp.Environment.Name =

The name or ID of the container app environment to deploy to.

Azure Location

Template.Azure.Location.Name =

The location in which to create the container app environment.

Container Name

Template.Azure.Container.Name =

The name of the container to create/update. If you want to use the image name, specify #{Octopus.Action.Package[Template.Azure.Container.Image].PackageId}

Container Image

Template.Azure.Container.Image =

Select the container image to deploy.

Environment variables

Template.Azure.Container.Variables =

JSON formatted key/value pair of environment variables to pass to the container. This supports use of OctoStache.

[
  {
    "name": "ConnectionStrings__CatalogConnection",
    "value": "Server=#{Project.SQL.DNS},1433;Integrated Security=true;Initial Catalog=#{Project.Catalog.Database.Name};User Id=#{Project.SQL.Admin.Username};Password=#{Project.SQL.Admin.Password};Trusted_Connection=false;Trust Server Certificate=True;"
  },
 {
  "name": "MyPasswordFromSecret",
  "secretref": "Name of my secret"
 },
 ...
]

Secrets

Template.Azure.Container.Secrets =

JSON formatted key/value pair of secrets to create/update. This supports use of OctoStache.

Note: The name of the secret must be lowercase.

[
  {
    "name": "mysecret",
    "value": "#{Project.SQL.Admin.Password}"
  },
 ...
]

Resource CPU

Template.Azure.Container.Cpu =

The amount of CPU to allocate to the container app.

Example: 0.5

Resource Memory

Template.Azure.Container.Memory =

The amount of memory to allocate to the container app.

Examples: 250Mb or 4.0Gi

Container Ingress Port

Template.Azure.Container.Ingress.Port =

The port to allow traffic to the container.

External Ingress

Template.Azure.Container.ExternalIngress = False

Whether the ingress is externally accessible or not.

Script body

Steps based on this template will execute the following PowerShell script.

# Define functions
function Get-ModuleInstalled
{
    # Define parameters
    param(
        $PowerShellModuleName
    )

    # Check to see if the module is installed
    if ($null -ne (Get-Module -ListAvailable -Name $PowerShellModuleName))
    {
        # It is installed
        return $true
    }
    else
    {
        # Module not installed
        return $false
    }
}

function Get-NugetPackageProviderNotInstalled
{
	# See if the nuget package provider has been installed
    return ($null -eq (Get-PackageProvider -ListAvailable -Name Nuget -ErrorAction SilentlyContinue))
}

function Install-PowerShellModule
{
    # Define parameters
    param(
        $PowerShellModuleName,
        $LocalModulesPath
    )

	# Check to see if the package provider has been installed
    if ((Get-NugetPackageProviderNotInstalled) -ne $false)
    {
    	# Display that we need the nuget package provider
        Write-Host "Nuget package provider not found, installing ..."
        
        # Install Nuget package provider
        Install-PackageProvider -Name Nuget -Force
    }

	# Save the module in the temporary location
    Write-Host "Saving module $PowerShellModuleName to temporary folder ..."
    Save-Module -Name $PowerShellModuleName -Path $LocalModulesPath -Force
    Write-Host "Save successful!"
}

# Check to see if $IsWindows is available
if ($null -eq $IsWindows)
{
     Write-Host "Determining Operating System..."
    $IsWindows = ([System.Environment]::OSVersion.Platform -eq "Win32NT")
    $IsLinux = ([System.Environment]::OSVersion.Platform -eq "Unix")
}

# Check to see if it's running on Windows
if ($IsWindows)
{
	# Disable the progress bar so downloading files via Invoke-WebRequest are faster
    $ProgressPreference = 'SilentlyContinue'
}

if ($PSEdition -eq "Core") {
    $PSStyle.OutputRendering = "PlainText"
}

# Define PowerShell Modules path
$LocalModules = (New-Item "$PWD/modules" -ItemType Directory -Force).FullName
$env:PSModulePath = "$LocalModules$([IO.Path]::PathSeparator)$env:PSModulePath"
$azureModule = "Az.App"

# Get variables
$templateAzureAccountClient = $OctopusParameters['Template.Azure.Account.ClientId']
$templateAzureAccountPassword = $OctopusParameters['Template.Azure.Account.Password']
$templateAzureAccountTenantId = $OctopusParameters['Template.Azure.Account.TenantId']
$templateAzureResourceGroup = $OctopusParameters['Template.Azure.ResourceGroup.Name']
$templateAzureSubscriptionId = $OctopusParameters['Template.Azure.Account.SubscriptionId']
$templateEnvironmentName = $OctopusParameters['Template.ContainerApp.Environment.Name']
$templateAzureLocation = $OctopusParameters['Template.Azure.Location.Name']
$templateAzureContainer = $OctopusParameters['Template.Azure.Container.Image']
$templateAzureContainerIngressPort = $OctopusParameters['Template.Azure.Container.Ingress.Port']
$templateAzureContainerIngressExternal = $OctopusParameters['Template.Azure.Container.ExternalIngress']
$vmMetaData = $null
$secretRef = @()
$templateAzureContainerSecrets = $null

if (![string]::IsNullOrWhitespace($OctopusParameters['Template.Azure.Container.Variables']))
{
    $templateAzureContainerEnvVars = ($OctopusParameters['Template.Azure.Container.Variables'] | ConvertFrom-JSON)
}
else
{
	$templateAzureContainerEnvVars = $null
}

if (![string]::IsNullOrWhitespace($OctopusParameters['Template.Azure.Container.Secrets']))
{
    $templateAzureContainerSecrets = ($OctopusParameters['Template.Azure.Container.Secrets'] | ConvertFrom-JSON)
}
else
{
	$templateAzureContainerSecrets = $null
}

$templateAzureContainerCPU = $OctopusParameters['Template.Azure.Container.Cpu']
$templateAzureContainerMemory = $OctopusParameters['Template.Azure.Container.Memory']

# Check for required PowerShell module
Write-Host "Checking for module $azureModule ..."

if ((Get-ModuleInstalled -PowerShellModuleName $azureModule) -eq $false)
{
	# Install the module
    Install-PowerShellModule -PowerShellModuleName $azureModule -LocalModulesPath $LocalModules
}

# Import the necessary module
Write-Host "Importing module $azureModule ..."
Import-Module $azureModule

# Check to see if the account was specified
if (![string]::IsNullOrWhitespace($templateAzureAccountClient))
{
	# Login using the provided account
    Write-Host "Logging in as specified account ..."
    
	# Create credential object for az module
	$securePassword = ConvertTo-SecureString $templateAzureAccountPassword -AsPlainText -Force
	$azureCredentials = New-Object System.Management.Automation.PSCredential ($templateAzureAccountClient, $securePassword)  

    Connect-AzAccount -Credential $azureCredentials -ServicePrincipal -Tenant $templateAzureAccountTenantId | Out-Null
    
    Write-Host "Login successful!"
}
else
{
	Write-Host "Using machine Managed Identity ..."
    $vmMetaData = Invoke-RestMethod -Headers @{"Metadata"="true"} -Method GET -Uri "http://169.254.169.254/metadata/instance?api-version=2021-02-01"
    
    Connect-AzAccount -Identity
    
    # Get Identity context
    $identityContext = Get-AzContext
    
    # Set variables
    $templateAzureSubscriptionId = $vmMetaData.compute.subscriptionId
    
    if ([string]::IsNullOrWhitespace($templateAzureAccountTenantId))
    {
    	$templateAzureAccountTenantId = $identityContext.Tenant
    }
    
    Set-AzContext -Tenant $templateAzureAccountTenantId | Out-Null
	Write-Host "Successfully set context for Managed Identity!"
}

# Check to see if the environment name is a / in it
if ($templateEnvironmentName.Contains("/") -ne $true)
{
	# Lookup environment id by name
    Write-Host "Looking up Managed Environment by Name ..."
    $templateEnvironmentName = (Get-AzContainerAppManagedEnv -ResourceGroupName $templateAzureResourceGroup -EnvName $templateEnvironmentName -SubscriptionId $templateAzureSubscriptionId).Id
}

# Build parameter list to pass to New-AzContainerAppTemplateObject
$PSBoundParameters.Add("Image", $OctopusParameters["Octopus.Action.Package[Template.Azure.Container.Image].Image"])
$PSBoundParameters.Add("Name", $OctopusParameters["Template.Azure.Container.Name"])

if (![string]::IsNullOrWhitespace($templateAzureContainerCPU))
{
    $PSBoundParameters.Add("ResourceCpu", "$templateAzureContainerCPU")
}

if (![string]::IsNullOrWhitespace($templateAzureContainerMemory))
{
    $PSBoundParameters.Add("ResourceMemory", "$templateAzureContainerMemory")
}

if ($null -ne $templateAzureContainerEnvVars)
{
    # Loop through list
    $envVars = @()
    foreach ($envVar in $templateAzureContainerEnvVars)
    {
    	$envEntry = @{}
        $envEntry.Add("Name", $envVar.Name)
        
        # Check for specific property
        if ($envVar.SecretRef)
        {
        	$envEntry.Add("SecretRef", $envVar.SecretRef)
        }
        else
        {
        	$envEntry.Add("Value", $envVar.Value)
        }
        
        # Add to collection
        $envVars += $envEntry
    }
    
    $PSBoundParameters.Add("Env", $envVars)
}

if ($null -ne $templateAzureContainerSecrets)
{
	# Loop through list
    foreach ($secret in $templateAzureContainerSecrets)
    {
        # Create new secret object and add to array
        $secretRef += New-AzContainerAppSecretObject -Name $secret.Name -Value $secret.Value
    }
}

# Create new container app
$containerDefinition = New-AzContainerAppTemplateObject @PSBoundParameters
$PSBoundParameters.Clear()

# Define ingress components
if (![string]::IsNullOrWhitespace($templateAzureContainerIngressPort))
{
	$PSBoundParameters.Add("IngressExternal", [System.Convert]::ToBoolean($templateAzureContainerIngressExternal))
    $PSBoundParameters.Add("IngressTargetPort", $templateAzureContainerIngressPort)
}

# Check the image
if ($OctopusParameters["Octopus.Action.Package[Template.Azure.Container.Image].Image"].Contains("azurecr.io"))
{
	# Define local parameters
    $registryCredentials = @{}
    $registrySecret = @{}
    
    # Accessing an ACR repository, configure credentials
    if (![string]::IsNullOrWhitespace($templateAzureAccountClient))
    {

		# Use configured client, name must be lower case
        $registryCredentials.Add("Username", $templateAzureAccountClient)
        $registryCredentials.Add("PasswordSecretRef", "clientpassword")

		$secretRef += New-AzContainerAppSecretObject -Name "clientpassword" -Value $templateAzureAccountPassword
    }
    else
    {
    	# Using Managed Identity
        $registryCredentials.Add("Identity", "system")
        
    }
    
    $registryServer = $OctopusParameters["Octopus.Action.Package[Template.Azure.Container.Image].Image"]
    $registryServer = $registryServer.Substring(0, $registryServer.IndexOf("/"))
    $registryCredentials.Add("Server", $registryServer)
       
    # Add credentials
    $PSBoundParameters.Add("Registry", $registryCredentials)
}

# Define secrets component
if ($secretRef.Count -gt 0)
{
	# Add to parameters
    $PSBoundParameters.Add("Secret", $secretRef)
}

# Create new configuration object
Write-Host "Creating new Configuration Object ..."
$configurationObject = New-AzContainerAppConfigurationObject @PSBoundParameters
$PSBoundParameters.Clear()

# Define parameters
$PSBoundParameters.Add("Name", $OctopusParameters["Template.Azure.Container.Name"])
$PSBoundParameters.Add("TemplateContainer", $containerDefinition)
$PSBoundParameters.Add("ResourceGroupName", $templateAzureResourceGroup)
$PSBoundParameters.Add("Configuration", $configurationObject)


# Check to see if the container app already exists
$containerApp = Get-AzContainerApp -Name $OctopusParameters["Template.Azure.Container.Name"] -ResourceGroupName $templateAzureResourceGroup
if ($null -eq $containerApp)
{
	# Add parameters required for creating container app
	$PSBoundParameters.Add("EnvironmentId", $templateEnvironmentName)
	$PSBoundParameters.Add("Location", $templateAzureLocation)
	
	# Deploy container
    Write-Host "Creating new container app ..."
	New-AzContainerApp @PSBoundParameters
}
else
{
	Write-Host "Updating existing container app ..."
    Update-AzContainerApp @PSBoundParameters
}

Provided under the Apache License version 2.0.

Report an issue

To use this template in Octopus Deploy, copy the JSON below and paste it into the Library → Step templates → Import dialog.

{
  "Id": "db701b9a-5dbe-477e-b820-07f9e354f634",
  "Name": "Azure - Deploy Container App",
  "Description": "Deploys a container to an Azure Container App Environment",
  "Version": 5,
  "ExportedAt": "2023-07-05T15:57:10.891Z",
  "ActionType": "Octopus.Script",
  "Author": "twerthi",
  "Packages": [
    {
      "Id": "636b2191-09f6-4f86-a59c-97e1891475db",
      "Name": "Template.Azure.Container.Image",
      "PackageId": null,
      "FeedId": null,
      "AcquisitionLocation": "NotAcquired",
      "Properties": {
        "Extract": "False",
        "SelectionMode": "deferred",
        "PackageParameterName": "Template.Azure.Container.Image",
        "Purpose": ""
      }
    }
  ],
  "Parameters": [
    {
      "Id": "a1ae1b6c-99d0-4ee5-9c3c-08c345966842",
      "Name": "Template.Azure.ResourceGroup.Name",
      "Label": "Azure Resource Group Name",
      "HelpText": "Provide the resource group name to create the environment in.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "fb92b5d7-5e48-485d-9222-dc2c97b58535",
      "Name": "Template.Azure.Account.SubscriptionId",
      "Label": "Azure Account Subscription Id",
      "HelpText": "The subscription ID of the Azure account to use.  This value can be retrieved from an Azure Account variable type.  Add an Azure Account to your project , then assign the `SubscriptionNumber` property to for this entry.  Leave blank to use the Managed Identity.\n\nFor example, if your Azure Account variable is called MyAccount, the value for this input would be `#{MyAccount.SubscriptionNumber}`",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "e1ce647f-956e-48d1-8002-44d958f1c8a4",
      "Name": "Template.Azure.Account.ClientId",
      "Label": "Azure Account Client Id",
      "HelpText": "The subscription ID of the Azure account to use.  This value can be retrieved from an Azure Account variable type.  Add an Azure Account to your project , then assign the `Client` property to for this entry.   Leave blank to use the Managed Identity.\n\nFor example, if your Azure Account variable is called MyAccount, the value for this input would be `#{MyAccount.Client}`",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "a02e3fd0-9d91-4106-84a4-ff0d68e5de7c",
      "Name": "Template.Azure.Account.TenantId",
      "Label": "Azure Account Tenant Id",
      "HelpText": "The subscription ID of the Azure account to use.  This value can be retrieved from an Azure Account variable type.  Add an Azure Account to your project , then assign the `TenantId` property to for this entry.   If blank, it will use the Managed Identity tenant.\n\nFor example, if your Azure Account variable is called MyAccount, the value for this input would be `#{MyAccount.TenantId}`",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "7bd73320-1889-41eb-a1c0-c8788e5eed9d",
      "Name": "Template.Azure.Account.Password",
      "Label": "Azure Account Password",
      "HelpText": "The subscription ID of the Azure account to use.  This value can be retrieved from an Azure Account variable type.  Add an Azure Account to your project , then assign the `Password` property to for this entry.   Leave blank to use the Managed Identity.\n\nFor example, if your Azure Account variable is called MyAccount, the value for this input would be `#{MyAccount.Password}`",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "Sensitive"
      }
    },
    {
      "Id": "f7fafc7e-2658-4a6f-ac30-7d9f738b9f52",
      "Name": "Template.ContainerApp.Environment.Name",
      "Label": "Container App Environment Name",
      "HelpText": "The name or ID of the container app environment to deploy to.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "31f02b7e-4822-49de-97dc-ee981b12268c",
      "Name": "Template.Azure.Location.Name",
      "Label": "Azure Location",
      "HelpText": "The location in which to create the container app environment.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "d74123b0-4104-45b9-ae12-b1f808b8b948",
      "Name": "Template.Azure.Container.Name",
      "Label": "Container Name",
      "HelpText": "The name of the container to create/update.  If you want to use the image name, specify `#{Octopus.Action.Package[Template.Azure.Container.Image].PackageId}`",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "1bdb6d31-4b97-4eab-aca3-5a60891b3455",
      "Name": "Template.Azure.Container.Image",
      "Label": "Container Image",
      "HelpText": "Select the container image to deploy.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "Package"
      }
    },
    {
      "Id": "9e7aa5cf-9bab-4303-a851-1b8757f2f486",
      "Name": "Template.Azure.Container.Variables",
      "Label": "Environment variables",
      "HelpText": "JSON formatted key/value pair of environment variables to pass to the container.  This supports use of OctoStache.\n\n```\n[\n  {\n    \"name\": \"ConnectionStrings__CatalogConnection\",\n    \"value\": \"Server=#{Project.SQL.DNS},1433;Integrated Security=true;Initial Catalog=#{Project.Catalog.Database.Name};User Id=#{Project.SQL.Admin.Username};Password=#{Project.SQL.Admin.Password};Trusted_Connection=false;Trust Server Certificate=True;\"\n  },\n {\n  \"name\": \"MyPasswordFromSecret\",\n  \"secretref\": \"Name of my secret\"\n },\n ...\n]\n```",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "MultiLineText"
      }
    },
    {
      "Id": "498f308d-a94c-4364-b0be-8d739ac63f4e",
      "Name": "Template.Azure.Container.Secrets",
      "Label": "Secrets",
      "HelpText": "JSON formatted key/value pair of secrets to create/update.  This supports use of OctoStache.\n\n**Note:** The name of the secret must be lowercase.\n\n```\n[\n  {\n    \"name\": \"mysecret\",\n    \"value\": \"#{Project.SQL.Admin.Password}\"\n  },\n ...\n]\n```",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "MultiLineText"
      }
    },
    {
      "Id": "20e3e428-62b9-4eef-9cdc-780a0eedee79",
      "Name": "Template.Azure.Container.Cpu",
      "Label": "Resource CPU",
      "HelpText": "The amount of CPU to allocate to the container app.\n\nExample: 0.5",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "d804f749-625e-4ca5-9bef-13fc2c2a463c",
      "Name": "Template.Azure.Container.Memory",
      "Label": "Resource Memory",
      "HelpText": "The amount of memory to allocate to the container app.\n\nExamples: 250Mb or 4.0Gi",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "a506b328-4e10-49fe-947b-6623d4d392c1",
      "Name": "Template.Azure.Container.Ingress.Port",
      "Label": "Container Ingress Port",
      "HelpText": "The port to allow traffic to the container.",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      }
    },
    {
      "Id": "682d68a4-f1eb-469c-9557-9577ed9f1505",
      "Name": "Template.Azure.Container.ExternalIngress",
      "Label": "External Ingress",
      "HelpText": "Whether the ingress is externally accessible or not.",
      "DefaultValue": "False",
      "DisplaySettings": {
        "Octopus.ControlType": "Checkbox"
      }
    }
  ],
  "Properties": {
    "Octopus.Action.Script.ScriptSource": "Inline",
    "Octopus.Action.Script.Syntax": "PowerShell",
    "Octopus.Action.Script.ScriptBody": "# Define functions\nfunction Get-ModuleInstalled\n{\n    # Define parameters\n    param(\n        $PowerShellModuleName\n    )\n\n    # Check to see if the module is installed\n    if ($null -ne (Get-Module -ListAvailable -Name $PowerShellModuleName))\n    {\n        # It is installed\n        return $true\n    }\n    else\n    {\n        # Module not installed\n        return $false\n    }\n}\n\nfunction Get-NugetPackageProviderNotInstalled\n{\n\t# See if the nuget package provider has been installed\n    return ($null -eq (Get-PackageProvider -ListAvailable -Name Nuget -ErrorAction SilentlyContinue))\n}\n\nfunction Install-PowerShellModule\n{\n    # Define parameters\n    param(\n        $PowerShellModuleName,\n        $LocalModulesPath\n    )\n\n\t# Check to see if the package provider has been installed\n    if ((Get-NugetPackageProviderNotInstalled) -ne $false)\n    {\n    \t# Display that we need the nuget package provider\n        Write-Host \"Nuget package provider not found, installing ...\"\n        \n        # Install Nuget package provider\n        Install-PackageProvider -Name Nuget -Force\n    }\n\n\t# Save the module in the temporary location\n    Write-Host \"Saving module $PowerShellModuleName to temporary folder ...\"\n    Save-Module -Name $PowerShellModuleName -Path $LocalModulesPath -Force\n    Write-Host \"Save successful!\"\n}\n\n# Check to see if $IsWindows is available\nif ($null -eq $IsWindows)\n{\n     Write-Host \"Determining Operating System...\"\n    $IsWindows = ([System.Environment]::OSVersion.Platform -eq \"Win32NT\")\n    $IsLinux = ([System.Environment]::OSVersion.Platform -eq \"Unix\")\n}\n\n# Check to see if it's running on Windows\nif ($IsWindows)\n{\n\t# Disable the progress bar so downloading files via Invoke-WebRequest are faster\n    $ProgressPreference = 'SilentlyContinue'\n}\n\nif ($PSEdition -eq \"Core\") {\n    $PSStyle.OutputRendering = \"PlainText\"\n}\n\n# Define PowerShell Modules path\n$LocalModules = (New-Item \"$PWD/modules\" -ItemType Directory -Force).FullName\n$env:PSModulePath = \"$LocalModules$([IO.Path]::PathSeparator)$env:PSModulePath\"\n$azureModule = \"Az.App\"\n\n# Get variables\n$templateAzureAccountClient = $OctopusParameters['Template.Azure.Account.ClientId']\n$templateAzureAccountPassword = $OctopusParameters['Template.Azure.Account.Password']\n$templateAzureAccountTenantId = $OctopusParameters['Template.Azure.Account.TenantId']\n$templateAzureResourceGroup = $OctopusParameters['Template.Azure.ResourceGroup.Name']\n$templateAzureSubscriptionId = $OctopusParameters['Template.Azure.Account.SubscriptionId']\n$templateEnvironmentName = $OctopusParameters['Template.ContainerApp.Environment.Name']\n$templateAzureLocation = $OctopusParameters['Template.Azure.Location.Name']\n$templateAzureContainer = $OctopusParameters['Template.Azure.Container.Image']\n$templateAzureContainerIngressPort = $OctopusParameters['Template.Azure.Container.Ingress.Port']\n$templateAzureContainerIngressExternal = $OctopusParameters['Template.Azure.Container.ExternalIngress']\n$vmMetaData = $null\n$secretRef = @()\n$templateAzureContainerSecrets = $null\n\nif (![string]::IsNullOrWhitespace($OctopusParameters['Template.Azure.Container.Variables']))\n{\n    $templateAzureContainerEnvVars = ($OctopusParameters['Template.Azure.Container.Variables'] | ConvertFrom-JSON)\n}\nelse\n{\n\t$templateAzureContainerEnvVars = $null\n}\n\nif (![string]::IsNullOrWhitespace($OctopusParameters['Template.Azure.Container.Secrets']))\n{\n    $templateAzureContainerSecrets = ($OctopusParameters['Template.Azure.Container.Secrets'] | ConvertFrom-JSON)\n}\nelse\n{\n\t$templateAzureContainerSecrets = $null\n}\n\n$templateAzureContainerCPU = $OctopusParameters['Template.Azure.Container.Cpu']\n$templateAzureContainerMemory = $OctopusParameters['Template.Azure.Container.Memory']\n\n# Check for required PowerShell module\nWrite-Host \"Checking for module $azureModule ...\"\n\nif ((Get-ModuleInstalled -PowerShellModuleName $azureModule) -eq $false)\n{\n\t# Install the module\n    Install-PowerShellModule -PowerShellModuleName $azureModule -LocalModulesPath $LocalModules\n}\n\n# Import the necessary module\nWrite-Host \"Importing module $azureModule ...\"\nImport-Module $azureModule\n\n# Check to see if the account was specified\nif (![string]::IsNullOrWhitespace($templateAzureAccountClient))\n{\n\t# Login using the provided account\n    Write-Host \"Logging in as specified account ...\"\n    \n\t# Create credential object for az module\n\t$securePassword = ConvertTo-SecureString $templateAzureAccountPassword -AsPlainText -Force\n\t$azureCredentials = New-Object System.Management.Automation.PSCredential ($templateAzureAccountClient, $securePassword)  \n\n    Connect-AzAccount -Credential $azureCredentials -ServicePrincipal -Tenant $templateAzureAccountTenantId | Out-Null\n    \n    Write-Host \"Login successful!\"\n}\nelse\n{\n\tWrite-Host \"Using machine Managed Identity ...\"\n    $vmMetaData = Invoke-RestMethod -Headers @{\"Metadata\"=\"true\"} -Method GET -Uri \"http://169.254.169.254/metadata/instance?api-version=2021-02-01\"\n    \n    Connect-AzAccount -Identity\n    \n    # Get Identity context\n    $identityContext = Get-AzContext\n    \n    # Set variables\n    $templateAzureSubscriptionId = $vmMetaData.compute.subscriptionId\n    \n    if ([string]::IsNullOrWhitespace($templateAzureAccountTenantId))\n    {\n    \t$templateAzureAccountTenantId = $identityContext.Tenant\n    }\n    \n    Set-AzContext -Tenant $templateAzureAccountTenantId | Out-Null\n\tWrite-Host \"Successfully set context for Managed Identity!\"\n}\n\n# Check to see if the environment name is a / in it\nif ($templateEnvironmentName.Contains(\"/\") -ne $true)\n{\n\t# Lookup environment id by name\n    Write-Host \"Looking up Managed Environment by Name ...\"\n    $templateEnvironmentName = (Get-AzContainerAppManagedEnv -ResourceGroupName $templateAzureResourceGroup -EnvName $templateEnvironmentName -SubscriptionId $templateAzureSubscriptionId).Id\n}\n\n# Build parameter list to pass to New-AzContainerAppTemplateObject\n$PSBoundParameters.Add(\"Image\", $OctopusParameters[\"Octopus.Action.Package[Template.Azure.Container.Image].Image\"])\n$PSBoundParameters.Add(\"Name\", $OctopusParameters[\"Template.Azure.Container.Name\"])\n\nif (![string]::IsNullOrWhitespace($templateAzureContainerCPU))\n{\n    $PSBoundParameters.Add(\"ResourceCpu\", \"$templateAzureContainerCPU\")\n}\n\nif (![string]::IsNullOrWhitespace($templateAzureContainerMemory))\n{\n    $PSBoundParameters.Add(\"ResourceMemory\", \"$templateAzureContainerMemory\")\n}\n\nif ($null -ne $templateAzureContainerEnvVars)\n{\n    # Loop through list\n    $envVars = @()\n    foreach ($envVar in $templateAzureContainerEnvVars)\n    {\n    \t$envEntry = @{}\n        $envEntry.Add(\"Name\", $envVar.Name)\n        \n        # Check for specific property\n        if ($envVar.SecretRef)\n        {\n        \t$envEntry.Add(\"SecretRef\", $envVar.SecretRef)\n        }\n        else\n        {\n        \t$envEntry.Add(\"Value\", $envVar.Value)\n        }\n        \n        # Add to collection\n        $envVars += $envEntry\n    }\n    \n    $PSBoundParameters.Add(\"Env\", $envVars)\n}\n\nif ($null -ne $templateAzureContainerSecrets)\n{\n\t# Loop through list\n    foreach ($secret in $templateAzureContainerSecrets)\n    {\n        # Create new secret object and add to array\n        $secretRef += New-AzContainerAppSecretObject -Name $secret.Name -Value $secret.Value\n    }\n}\n\n# Create new container app\n$containerDefinition = New-AzContainerAppTemplateObject @PSBoundParameters\n$PSBoundParameters.Clear()\n\n# Define ingress components\nif (![string]::IsNullOrWhitespace($templateAzureContainerIngressPort))\n{\n\t$PSBoundParameters.Add(\"IngressExternal\", [System.Convert]::ToBoolean($templateAzureContainerIngressExternal))\n    $PSBoundParameters.Add(\"IngressTargetPort\", $templateAzureContainerIngressPort)\n}\n\n# Check the image\nif ($OctopusParameters[\"Octopus.Action.Package[Template.Azure.Container.Image].Image\"].Contains(\"azurecr.io\"))\n{\n\t# Define local parameters\n    $registryCredentials = @{}\n    $registrySecret = @{}\n    \n    # Accessing an ACR repository, configure credentials\n    if (![string]::IsNullOrWhitespace($templateAzureAccountClient))\n    {\n\n\t\t# Use configured client, name must be lower case\n        $registryCredentials.Add(\"Username\", $templateAzureAccountClient)\n        $registryCredentials.Add(\"PasswordSecretRef\", \"clientpassword\")\n\n\t\t$secretRef += New-AzContainerAppSecretObject -Name \"clientpassword\" -Value $templateAzureAccountPassword\n    }\n    else\n    {\n    \t# Using Managed Identity\n        $registryCredentials.Add(\"Identity\", \"system\")\n        \n    }\n    \n    $registryServer = $OctopusParameters[\"Octopus.Action.Package[Template.Azure.Container.Image].Image\"]\n    $registryServer = $registryServer.Substring(0, $registryServer.IndexOf(\"/\"))\n    $registryCredentials.Add(\"Server\", $registryServer)\n       \n    # Add credentials\n    $PSBoundParameters.Add(\"Registry\", $registryCredentials)\n}\n\n# Define secrets component\nif ($secretRef.Count -gt 0)\n{\n\t# Add to parameters\n    $PSBoundParameters.Add(\"Secret\", $secretRef)\n}\n\n# Create new configuration object\nWrite-Host \"Creating new Configuration Object ...\"\n$configurationObject = New-AzContainerAppConfigurationObject @PSBoundParameters\n$PSBoundParameters.Clear()\n\n# Define parameters\n$PSBoundParameters.Add(\"Name\", $OctopusParameters[\"Template.Azure.Container.Name\"])\n$PSBoundParameters.Add(\"TemplateContainer\", $containerDefinition)\n$PSBoundParameters.Add(\"ResourceGroupName\", $templateAzureResourceGroup)\n$PSBoundParameters.Add(\"Configuration\", $configurationObject)\n\n\n# Check to see if the container app already exists\n$containerApp = Get-AzContainerApp -Name $OctopusParameters[\"Template.Azure.Container.Name\"] -ResourceGroupName $templateAzureResourceGroup\nif ($null -eq $containerApp)\n{\n\t# Add parameters required for creating container app\n\t$PSBoundParameters.Add(\"EnvironmentId\", $templateEnvironmentName)\n\t$PSBoundParameters.Add(\"Location\", $templateAzureLocation)\n\t\n\t# Deploy container\n    Write-Host \"Creating new container app ...\"\n\tNew-AzContainerApp @PSBoundParameters\n}\nelse\n{\n\tWrite-Host \"Updating existing container app ...\"\n    Update-AzContainerApp @PSBoundParameters\n}\n"
  },
  "Category": "Azure",
  "HistoryUrl": "https://github.com/OctopusDeploy/Library/commits/master/step-templates//opt/buildagent/work/75443764cd38076d/step-templates/azure-deploy-containerapp.json",
  "Website": "/step-templates/db701b9a-5dbe-477e-b820-07f9e354f634",
  "Logo": "iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAMAAACahl6sAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADNQTFRF////AHjXf7vrv931QJrh7/f8EIDaIIncMJHfYKvmz+b3n8zw3+76j8Ttr9XycLPpUKLkkKvYFAAABGZJREFUeNrsnNmCqjoQRc1MEiD8/9cer7Yt2KBJZQC8ez07sKlKTQlcLgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzoUSnt8YxXlFuGHSbIaxvj+fip4btkLn1blkWLaF5v03yLhLOYlVuGYfMOMZzNGxCOzhjTJqFkXnjq3Dr1yyvPI3hGl3Ih3zzHHNKudRstRhX5O58vIcShY67Gq6EPIESlzUWvazaGAOGbvU7ArDu/g8M4o8opDZWvbvPzlL/MMBE8jT9T9W7PbAJlHPTBFRf9yVTEcs63msXz2UHLSgf650G/d5t+wjbxxB2UCMqGrk8/LFSD7uJMeNt5bcJCyQZyAe5Fo9KYfWS2flQrr4b4tpuzaeWjYs49rt9LHf9uZD7+VbyVi9EBNrjYjuq2sxQOrl+p+HuBVu45qvqfq691ttYFQ5KyKbyJgaIY/NGxrlWZwlwGvmvu1oY3PuAv0niTq6tZ78jk//9uc1r1r4lQki7y7sp2Tu4V1y2iLoqFTqi1lIGcpFiebrZNZ1dOkF0cCIlO8jQ47nCkam9Lilz9GhDF1I6XGLzfnhwDIIZVfI7+8SSgfHsijqXENOGJF5QorG4EcW0OrScqX/dDrXpr70Ut/BII+1OfECPuYz/NWxYmgrCsUskxPvyhgmrw+WGZ6lGTuOlIyCYWTFyWjpM5KIZRUIOwjRNYRQ6tZF9BXtk8hWAHPtLNJ727Fq0JSkC1FDRRF0Jalj0d5qVh2KEpM2TuSsCYTCT6ZkdmFYI9LrYp5QayWbo6NXlZwcRD/61pth5Fq5EX423QQxNjhqWvvklkljOLkYjrmphXPZOJOk6Pg7HKMsrtQKcowzZoK3rx1ZUelGMdQA/HaKkjAt2RgqpZeYqbNbH7Hp2ct4nqfSPOfe0ftiSTZJydOV6rG5bQbyLK+nRuCC0343PzDgiOXyQA5c14BTZi98uR/5KJ1SnatLdoO50WWBQZPTq0VgsklU3h932actuo17ayrHrb/3ykiegd3KbqF2wbV6RrlsJ07yLcpsWFTul9RyK6ZScr+tk7oNrFj0o7HQUlj4EiEvJ6rPLKSmlMZCrksl1OnLaRkxc+/HB1naMhNtT/6yM2bDs6azCRHrM3aVPN7aW8irD/10B8njpAMcsl8okXcdKrl4sPsLmQVy/Sj90ucPRc/d/Bxxj+dXSpCayen32D+hLi16MsIV8gfCXrYp6ySsiJKRUF0XXiLpVbFU+fNv4r7mOwhFsX4ZdwpSi1DYs2jb6ebZ9788cblTzMrYhu7sf/17IFdtuviJ2ioHA6pMHkoH4CLUeMBU7iGkxuM/YgcdderF9ibRdc7O982F1HpYhjfWUe+x5a6pjop9iNLfoePvlsdZdTSMwfxSmTY20Q0eHnUNzga1edeNmmqbg18aMVR1L9vwSXHF9TfIWBxpKLs2hj3eQeBC0USvp2HHF3eIkRdhFOd6ER8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA/I/4J8AAo/80BciBec4AAAAASUVORK5CYII=",
  "$Meta": {
    "Type": "ActionTemplate"
  }
}

History

Page updated on Wednesday, July 5, 2023