diff --git a/container-azure-csharp/${PROJECT}.csproj b/container-azure-csharp/${PROJECT}.csproj
new file mode 100644
index 000000000..d2bf051ce
--- /dev/null
+++ b/container-azure-csharp/${PROJECT}.csproj
@@ -0,0 +1,17 @@
+
+
+ Exe
+ net6.0
+ enable
+
+
+ app/**;$(DefaultItemExcludes)
+
+
+
+
+
+
+
+
+
diff --git a/container-azure-csharp/.gitignore b/container-azure-csharp/.gitignore
new file mode 100644
index 000000000..e64527066
--- /dev/null
+++ b/container-azure-csharp/.gitignore
@@ -0,0 +1,353 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/container-azure-csharp/Program.cs b/container-azure-csharp/Program.cs
new file mode 100644
index 000000000..acb7c31fa
--- /dev/null
+++ b/container-azure-csharp/Program.cs
@@ -0,0 +1,120 @@
+using System;
+using System.Collections.Generic;
+using Pulumi;
+using AzureNative = Pulumi.AzureNative;
+using Docker = Pulumi.Docker;
+using Random = Pulumi.Random;
+
+return await Pulumi.Deployment.RunAsync(() =>
+{
+ // Import the program's configuration settings.
+ var config = new Config();
+ var appPath = config.Get("appPath") ?? "./app";
+ var imageName = config.Get("imageName") ?? "my-app";
+ var imageTag = config.Get("imageTag") ?? "latest";
+ var containerPort = config.GetInt32("containerPort") ?? 80;
+ var cpu = config.GetInt32("cpu") ?? 1;
+ var memory = config.GetInt32("memory") ?? 2;
+
+ // Create a resource group for the container registry.
+ var resourceGroup = new AzureNative.Resources.ResourceGroup("resource-group");
+
+ // Create a container registry.
+ var registry = new AzureNative.ContainerRegistry.Registry("registry", new()
+ {
+ ResourceGroupName = resourceGroup.Name,
+ AdminUserEnabled = true,
+ Sku = new AzureNative.ContainerRegistry.Inputs.SkuArgs {
+ Name = AzureNative.ContainerRegistry.SkuName.Basic,
+ },
+ });
+
+ // Fetch login credentials for the registry.
+ var credentials = AzureNative.ContainerRegistry.ListRegistryCredentials.Invoke(new()
+ {
+ ResourceGroupName = resourceGroup.Name,
+ RegistryName = registry.Name,
+ });
+ var registryUsername = credentials.Apply(result => result.Username!);
+ var registryPassword = credentials.Apply(result => result.Passwords[0]!.Value!);
+
+ // Create a container image for the service.
+ var image = new Docker.Image("image", new()
+ {
+ ImageName = Pulumi.Output.Format($"{registry.LoginServer}/{imageName}:{imageTag}"),
+ Build = new Docker.DockerBuild {
+ Context = appPath,
+ },
+ Registry = new Docker.ImageRegistry {
+ Server = registry.LoginServer,
+ Username = registryUsername,
+ Password = registryPassword,
+ },
+ });
+
+ // Use a random string to give the service a unique DNS name.
+ var dnsName = new Random.RandomString("dns-name", new()
+ {
+ Length = 8,
+ Special = false,
+ }).Result.Apply(result => $"{imageName}-{result.ToLower()}");
+
+ // Create a container group for the service that makes it publicly accessible.
+ var containerGroup = new AzureNative.ContainerInstance.ContainerGroup("container-group", new()
+ {
+ ResourceGroupName = resourceGroup.Name,
+ OsType = "linux",
+ RestartPolicy = "always",
+ ImageRegistryCredentials = new AzureNative.ContainerInstance.Inputs.ImageRegistryCredentialArgs {
+ Server = registry.LoginServer,
+ Username = registryUsername,
+ Password = registryPassword,
+ },
+ Containers = new[]
+ {
+ new AzureNative.ContainerInstance.Inputs.ContainerArgs {
+ Name = imageName,
+ Image = image.ImageName,
+ Ports = new[]
+ {
+ new AzureNative.ContainerInstance.Inputs.ContainerPortArgs {
+ Port = containerPort,
+ Protocol = "tcp",
+ },
+ },
+ EnvironmentVariables = new[]
+ {
+ new AzureNative.ContainerInstance.Inputs.EnvironmentVariableArgs {
+ Name = "ASPNETCORE_URLS",
+ Value = $"http://0.0.0.0:{containerPort}",
+ },
+ },
+ Resources = new AzureNative.ContainerInstance.Inputs.ResourceRequirementsArgs {
+ Requests = new AzureNative.ContainerInstance.Inputs.ResourceRequestsArgs {
+ Cpu = cpu,
+ MemoryInGB = memory,
+ },
+ },
+ },
+ },
+ IpAddress = new AzureNative.ContainerInstance.Inputs.IpAddressArgs {
+ Type = AzureNative.ContainerInstance.ContainerGroupIpAddressType.Public,
+ DnsNameLabel = dnsName,
+ Ports = new[]
+ {
+ new AzureNative.ContainerInstance.Inputs.PortArgs {
+ Port = containerPort,
+ Protocol = "tcp",
+ },
+ },
+ }
+ });
+
+ // Export the service's IP address, hostname, and fully-qualified URL.
+ return new Dictionary
+ {
+ ["hostname"] = containerGroup.IpAddress.Apply(addr => addr!.Fqdn),
+ ["ip"] = containerGroup.IpAddress.Apply(addr => addr!.Ip),
+ ["url"] = containerGroup.IpAddress.Apply(addr => $"http://{addr!.Fqdn}:{containerPort}"),
+ };
+});
diff --git a/container-azure-csharp/Pulumi.yaml b/container-azure-csharp/Pulumi.yaml
new file mode 100644
index 000000000..a4959b38a
--- /dev/null
+++ b/container-azure-csharp/Pulumi.yaml
@@ -0,0 +1,21 @@
+name: ${PROJECT}
+description: ${DESCRIPTION}
+runtime: dotnet
+template:
+ description: A containerized service on Azure Container Instances
+ config:
+ azure-native:location:
+ description: The Azure region to deploy into
+ default: WestUS
+ appPath:
+ description: The path to the container application to deploy
+ default: app
+ containerPort:
+ description: The port to expose on the container
+ default: 80
+ cpu:
+ description: The number of CPU cores to allocate on the container
+ default: 1
+ memory:
+ description: The amount of memory, in GB, to allocate on the container
+ default: 2
diff --git a/container-azure-csharp/app/App.csproj b/container-azure-csharp/app/App.csproj
new file mode 100644
index 000000000..855c69078
--- /dev/null
+++ b/container-azure-csharp/app/App.csproj
@@ -0,0 +1,8 @@
+
+
+ net6.0
+ enable
+ enable
+
+
+
diff --git a/container-azure-csharp/app/Dockerfile b/container-azure-csharp/app/Dockerfile
new file mode 100644
index 000000000..f1b8147d2
--- /dev/null
+++ b/container-azure-csharp/app/Dockerfile
@@ -0,0 +1,15 @@
+FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
+WORKDIR /app
+
+COPY *.csproj ./
+RUN dotnet restore
+
+COPY . .
+RUN dotnet publish -c Release -o out
+
+FROM mcr.microsoft.com/dotnet/aspnet:6.0 as base
+WORKDIR /app
+
+COPY --from=build /app/out .
+
+ENTRYPOINT ["dotnet", "App.dll"]
diff --git a/container-azure-csharp/app/Program.cs b/container-azure-csharp/app/Program.cs
new file mode 100644
index 000000000..24b5f3422
--- /dev/null
+++ b/container-azure-csharp/app/Program.cs
@@ -0,0 +1,12 @@
+var builder = WebApplication.CreateBuilder(args);
+var app = builder.Build();
+
+app.MapGet("/", async (context) =>
+{
+ await context.Response.WriteAsJsonAsync(new
+ {
+ message = "Hello, world!"
+ });
+});
+
+app.Run();
diff --git a/container-azure-csharp/app/Properties/launchSettings.json b/container-azure-csharp/app/Properties/launchSettings.json
new file mode 100644
index 000000000..9911d5af1
--- /dev/null
+++ b/container-azure-csharp/app/Properties/launchSettings.json
@@ -0,0 +1,28 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:33060",
+ "sslPort": 44325
+ }
+ },
+ "profiles": {
+ "container_azure": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "applicationUrl": "https://localhost:7264;http://localhost:5219",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/container-azure-csharp/app/appsettings.Development.json b/container-azure-csharp/app/appsettings.Development.json
new file mode 100644
index 000000000..0e7f4af09
--- /dev/null
+++ b/container-azure-csharp/app/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/container-azure-csharp/app/appsettings.json b/container-azure-csharp/app/appsettings.json
new file mode 100644
index 000000000..a9a1bac2c
--- /dev/null
+++ b/container-azure-csharp/app/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/container-azure-go/Pulumi.yaml b/container-azure-go/Pulumi.yaml
new file mode 100644
index 000000000..a348925f8
--- /dev/null
+++ b/container-azure-go/Pulumi.yaml
@@ -0,0 +1,21 @@
+name: ${PROJECT}
+description: ${DESCRIPTION}
+runtime: go
+template:
+ description: A containerized service on Azure Container Instances
+ config:
+ azure-native:location:
+ description: The Azure region to deploy into
+ default: WestUS
+ appPath:
+ description: The path to the container application to deploy
+ default: app
+ containerPort:
+ description: The port to expose on the container
+ default: 80
+ cpu:
+ description: The number of CPU cores to allocate on the container
+ default: 1
+ memory:
+ description: The amount of memory, in GB, to allocate on the container
+ default: 2
diff --git a/container-azure-go/app/Dockerfile b/container-azure-go/app/Dockerfile
new file mode 100644
index 000000000..9ed7d590c
--- /dev/null
+++ b/container-azure-go/app/Dockerfile
@@ -0,0 +1,10 @@
+FROM golang
+WORKDIR /usr/src/app
+
+COPY go.* ./
+RUN go mod download
+
+COPY . .
+RUN go build -o /app
+
+ENTRYPOINT ["/app"]
diff --git a/container-azure-go/app/go.mod b/container-azure-go/app/go.mod
new file mode 100644
index 000000000..b16f0d7c5
--- /dev/null
+++ b/container-azure-go/app/go.mod
@@ -0,0 +1,3 @@
+module app
+
+go 1.18
diff --git a/container-azure-go/app/main.go b/container-azure-go/app/main.go
new file mode 100644
index 000000000..586ae4b82
--- /dev/null
+++ b/container-azure-go/app/main.go
@@ -0,0 +1,34 @@
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "log"
+ "net/http"
+ "os"
+)
+
+type AppResponse struct {
+ Message string `json:"message"`
+}
+
+func main() {
+
+ http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+ response := AppResponse{
+ Message: "Hello, world!",
+ }
+
+ body, err := json.Marshal(response)
+ if err != nil {
+ http.Error(w, "Internal Server Error", http.StatusInternalServerError)
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ fmt.Fprint(w, string(body))
+ })
+
+ if err := http.ListenAndServe(fmt.Sprintf(":%s", os.Getenv("PORT")), nil); err != nil {
+ log.Fatal(err)
+ }
+}
diff --git a/container-azure-go/go.mod b/container-azure-go/go.mod
new file mode 100644
index 000000000..2225672a8
--- /dev/null
+++ b/container-azure-go/go.mod
@@ -0,0 +1,84 @@
+module azure-container-go
+
+go 1.17
+
+require (
+ github.com/pulumi/pulumi-azure-native/sdk v1.74.0
+ github.com/pulumi/pulumi-random/sdk v1.7.0
+ github.com/pulumi/pulumi-random/sdk/v4 v4.8.2
+ github.com/pulumi/pulumi/sdk/v3 v3.42.0
+)
+
+require (
+ github.com/Masterminds/semver v1.5.0 // indirect
+ github.com/acomagu/bufpipe v1.0.3 // indirect
+ github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
+ github.com/blang/semver v3.5.1+incompatible // indirect
+ github.com/go-git/gcfg v1.5.0 // indirect
+ github.com/go-git/go-billy/v5 v5.3.1 // indirect
+ github.com/go-git/go-git/v5 v5.4.2 // indirect
+ github.com/gofrs/flock v0.7.1 // indirect
+ github.com/gogo/protobuf v1.3.2 // indirect
+ github.com/golang/protobuf v1.5.2 // indirect
+ github.com/google/go-cmp v0.5.8 // indirect
+ github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
+ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
+ github.com/mitchellh/go-homedir v1.1.0 // indirect
+ github.com/mitchellh/go-ps v1.0.0 // indirect
+ github.com/pkg/errors v0.9.1 // indirect
+ github.com/pkg/term v1.1.0 // indirect
+ github.com/pulumi/pulumi/sdk v1.13.1 // indirect
+ github.com/spf13/cast v1.3.1 // indirect
+ github.com/spf13/pflag v1.0.5 // indirect
+ github.com/src-d/gcfg v1.4.0 // indirect
+ github.com/tweekmonster/luser v0.0.0-20161003172636-3fa38070dbd7 // indirect
+ github.com/xanzy/ssh-agent v0.3.2 // indirect
+ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
+ golang.org/x/text v0.3.7 // indirect
+ golang.org/x/tools v0.1.12 // indirect
+ google.golang.org/protobuf v1.28.1 // indirect
+ gopkg.in/src-d/go-billy.v4 v4.3.2 // indirect
+ gopkg.in/src-d/go-git.v4 v4.13.1 // indirect
+ gopkg.in/warnings.v0 v0.1.2 // indirect
+ gopkg.in/yaml.v2 v2.4.0 // indirect
+ lukechampine.com/frand v1.4.2 // indirect
+)
+
+require (
+ github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect
+ github.com/Microsoft/go-winio v0.6.0 // indirect
+ github.com/ProtonMail/go-crypto v0.0.0-20220930113650-c6815a8c17ad // indirect
+ github.com/cheggaaa/pb v1.0.29 // indirect
+ github.com/cloudflare/circl v1.2.0 // indirect
+ github.com/djherbis/times v1.5.0 // indirect
+ github.com/emirpasic/gods v1.18.1 // indirect
+ github.com/gofrs/uuid v4.3.0+incompatible // indirect
+ github.com/golang/glog v1.0.0 // indirect
+ github.com/hashicorp/errwrap v1.1.0 // indirect
+ github.com/hashicorp/go-multierror v1.1.1 // indirect
+ github.com/imdario/mergo v0.3.13 // indirect
+ github.com/inconshreveable/mousetrap v1.0.1 // indirect
+ github.com/kevinburke/ssh_config v1.2.0 // indirect
+ github.com/mattn/go-isatty v0.0.16 // indirect
+ github.com/mattn/go-runewidth v0.0.14 // indirect
+ github.com/opentracing/basictracer-go v1.1.0 // indirect
+ github.com/opentracing/opentracing-go v1.2.0 // indirect
+ github.com/pulumi/pulumi-docker/sdk/v3 v3.4.1
+ github.com/rivo/uniseg v0.4.2 // indirect
+ github.com/rogpeppe/go-internal v1.9.0 // indirect
+ github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 // indirect
+ github.com/santhosh-tekuri/jsonschema/v5 v5.0.1 // indirect
+ github.com/sergi/go-diff v1.2.0 // indirect
+ github.com/spf13/cobra v1.5.0 // indirect
+ github.com/texttheater/golang-levenshtein v1.0.1 // indirect
+ github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect
+ github.com/uber/jaeger-lib v2.4.1+incompatible // indirect
+ go.uber.org/atomic v1.10.0 // indirect
+ golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b // indirect
+ golang.org/x/net v0.0.0-20221004154528-8021a29435af // indirect
+ golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875 // indirect
+ golang.org/x/term v0.0.0-20220919170432-7a66f970e087 // indirect
+ google.golang.org/genproto v0.0.0-20220930163606-c98284e70a91 // indirect
+ google.golang.org/grpc v1.50.0 // indirect
+ sourcegraph.com/sourcegraph/appdash v0.0.0-20211028080628-e2786a622600 // indirect
+)
diff --git a/container-azure-go/main.go b/container-azure-go/main.go
new file mode 100644
index 000000000..13d53f7af
--- /dev/null
+++ b/container-azure-go/main.go
@@ -0,0 +1,155 @@
+package main
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/pulumi/pulumi-azure-native/sdk/go/azure/containerinstance"
+ "github.com/pulumi/pulumi-azure-native/sdk/go/azure/containerregistry"
+ "github.com/pulumi/pulumi-azure-native/sdk/go/azure/resources"
+ "github.com/pulumi/pulumi-docker/sdk/v3/go/docker"
+ "github.com/pulumi/pulumi-random/sdk/v4/go/random"
+ "github.com/pulumi/pulumi/sdk/v3/go/pulumi"
+ "github.com/pulumi/pulumi/sdk/v3/go/pulumi/config"
+)
+
+func main() {
+ pulumi.Run(func(ctx *pulumi.Context) error {
+
+ // Import the program's configuration settings.
+ cfg := config.New(ctx, "")
+ appPath := "./app"
+ if param := cfg.Get("appPath"); param != "" {
+ appPath = param
+ }
+ imageName := "my-app"
+ if param := cfg.Get("imageName"); param != "" {
+ imageName = param
+ }
+ imageTag := "latest"
+ if param := cfg.Get("imageTag"); param != "" {
+ imageName = param
+ }
+ containerPort := 80
+ if param := cfg.GetInt("containerPort"); param != 0 {
+ containerPort = param
+ }
+ cpu := 1
+ if param := cfg.GetInt("cpu"); param != 0 {
+ cpu = param
+ }
+ memory := 2
+ if param := cfg.GetInt("memory"); param != 0 {
+ memory = param
+ }
+
+ // Create a resource group for the container registry.
+ resourceGroup, err := resources.NewResourceGroup(ctx, "resource-group", nil)
+ if err != nil {
+ return err
+ }
+
+ // Create a container registry.
+ registry, err := containerregistry.NewRegistry(ctx, "registry", &containerregistry.RegistryArgs{
+ ResourceGroupName: resourceGroup.Name,
+ AdminUserEnabled: pulumi.Bool(true),
+ Sku: &containerregistry.SkuArgs{
+ Name: pulumi.String(containerregistry.SkuNameBasic),
+ },
+ })
+ if err != nil {
+ return err
+ }
+
+ // Fetch login credentials for the registry.
+ credentials := containerregistry.ListRegistryCredentialsOutput(ctx, containerregistry.ListRegistryCredentialsOutputArgs{
+ ResourceGroupName: resourceGroup.Name,
+ RegistryName: registry.Name,
+ })
+ registryUsername := credentials.Username().Elem()
+ registryPassword := credentials.Passwords().Index(pulumi.Int(0)).Value().Elem()
+
+ // Create a container image for the service.
+ image, err := docker.NewImage(ctx, "image", &docker.ImageArgs{
+ ImageName: pulumi.Sprintf("%s/%s:%s", registry.LoginServer, imageName, imageTag),
+ Build: docker.DockerBuildArgs{
+ Context: pulumi.String(appPath),
+ },
+ Registry: docker.ImageRegistryArgs{
+ Server: registry.LoginServer,
+ Username: registryUsername,
+ Password: registryPassword,
+ },
+ })
+ if err != nil {
+ return err
+ }
+
+ // Use a random string to give the service a unique DNS name.
+ dnsNameSuffix, err := random.NewRandomString(ctx, "dns-name-suffix", &random.RandomStringArgs{
+ Length: pulumi.Int(8),
+ Special: pulumi.Bool(false),
+ })
+ if err != nil {
+ return err
+ }
+ dnsName := dnsNameSuffix.Result.ApplyT(func(result string) string {
+ return fmt.Sprintf("%s-%s", imageName, strings.ToLower(result))
+ }).(pulumi.StringOutput)
+
+ // Create a container group for the service that makes it publicly accessible.
+ containerGroup, err := containerinstance.NewContainerGroup(ctx, "container-group", &containerinstance.ContainerGroupArgs{
+ ResourceGroupName: resourceGroup.Name,
+ OsType: pulumi.String("linux"),
+ RestartPolicy: pulumi.String("always"),
+ ImageRegistryCredentials: containerinstance.ImageRegistryCredentialArray{
+ containerinstance.ImageRegistryCredentialArgs{
+ Server: registry.LoginServer,
+ Username: registryUsername,
+ Password: registryPassword,
+ },
+ },
+ Containers: containerinstance.ContainerArray{
+ containerinstance.ContainerArgs{
+ Name: pulumi.String(imageName),
+ Image: image.ImageName,
+ Ports: containerinstance.ContainerPortArray{
+ containerinstance.ContainerPortArgs{
+ Port: pulumi.Int(containerPort),
+ Protocol: pulumi.String("tcp"),
+ },
+ },
+ EnvironmentVariables: containerinstance.EnvironmentVariableArray{
+ containerinstance.EnvironmentVariableArgs{
+ Name: pulumi.String("PORT"),
+ Value: pulumi.Sprintf("%d", containerPort),
+ },
+ },
+ Resources: containerinstance.ResourceRequirementsArgs{
+ Requests: containerinstance.ResourceRequestsArgs{
+ Cpu: pulumi.Float64(cpu),
+ MemoryInGB: pulumi.Float64(memory),
+ },
+ },
+ },
+ },
+ IpAddress: containerinstance.IpAddressArgs{
+ Type: pulumi.String("public"),
+ DnsNameLabel: dnsName,
+ Ports: containerinstance.PortArray{
+ containerinstance.PortArgs{
+ Port: pulumi.Int(containerPort),
+ Protocol: pulumi.String("tcp"),
+ },
+ },
+ },
+ })
+
+ // Export the service's IP address, hostname, and fully-qualified URL.
+ ctx.Export("ip", containerGroup.IpAddress.Elem().Ip())
+ ctx.Export("hostname", containerGroup.IpAddress.Elem().Fqdn())
+ ctx.Export("url", pulumi.Sprintf("http://%s:%d", containerGroup.IpAddress.Elem().Fqdn(), containerPort))
+
+ return nil
+ })
+}
diff --git a/container-azure-python/.gitignore b/container-azure-python/.gitignore
new file mode 100644
index 000000000..a3807e5bd
--- /dev/null
+++ b/container-azure-python/.gitignore
@@ -0,0 +1,2 @@
+*.pyc
+venv/
diff --git a/container-azure-python/Pulumi.yaml b/container-azure-python/Pulumi.yaml
new file mode 100644
index 000000000..4352fc81f
--- /dev/null
+++ b/container-azure-python/Pulumi.yaml
@@ -0,0 +1,24 @@
+name: ${PROJECT}
+description: ${DESCRIPTION}
+runtime:
+ name: python
+ options:
+ virtualenv: venv
+template:
+ description: A containerized service on Azure Container Instances
+ config:
+ azure-native:location:
+ description: The Azure region to deploy into
+ default: WestUS
+ appPath:
+ description: The path to the container application to deploy
+ default: app
+ containerPort:
+ description: The port to expose on the container
+ default: 80
+ cpu:
+ description: The number of CPU cores to allocate on the container
+ default: 1
+ memory:
+ description: The amount of memory, in GB, to allocate on the container
+ default: 2
diff --git a/container-azure-python/__main__.py b/container-azure-python/__main__.py
new file mode 100644
index 000000000..7547bfd3f
--- /dev/null
+++ b/container-azure-python/__main__.py
@@ -0,0 +1,125 @@
+import pulumi
+import pulumi_docker as docker
+import pulumi_random as random
+from pulumi_azure_native import resources, containerregistry, containerinstance
+
+# Import the program's configuration settings.
+config = pulumi.Config()
+app_path = config.get("appPath", "./app")
+image_name = config.get("imageName", "my-app")
+image_tag = config.get("imageTag", "latest")
+container_port = config.get_int("containerPort", 80)
+cpu = config.get_int("cpu", 1)
+memory = config.get_int("memory", 2)
+
+# Create a resource group for the container registry.
+resource_group = resources.ResourceGroup("resource-group")
+
+# Create a container registry.
+registry = containerregistry.Registry(
+ "registry",
+ containerregistry.RegistryArgs(
+ resource_group_name=resource_group.name,
+ admin_user_enabled=True,
+ sku=containerregistry.SkuArgs(
+ name=containerregistry.SkuName.BASIC,
+ ),
+ ),
+)
+
+# Fetch login credentials for the registry.
+credentials = containerregistry.list_registry_credentials_output(
+ resource_group_name=resource_group.name,
+ registry_name=registry.name,
+)
+
+registry_username = credentials.apply(lambda creds: creds.username)
+registry_password = credentials.apply(lambda creds: creds.passwords[0].value)
+
+# Create a container image for the service.
+image = docker.Image(
+ "image",
+ image_name=pulumi.Output.concat(registry.login_server, f"/{image_name}:{image_tag}"),
+ build=docker.DockerBuild(
+ context=app_path,
+ ),
+ registry=docker.ImageRegistry(
+ server=registry.login_server,
+ username=registry_username,
+ password=registry_password,
+ ),
+)
+
+# Use a random string to give the service a unique DNS name.
+dns_name = random.RandomString(
+ "dns-name",
+ random.RandomStringArgs(
+ length=8,
+ special=False,
+ ),
+).result.apply(lambda result: f"{image_name}-{result.lower()}")
+
+# Create a container group for the service that makes it publicly accessible.
+container_group = containerinstance.ContainerGroup(
+ "container-group",
+ containerinstance.ContainerGroupArgs(
+ resource_group_name=resource_group.name,
+ os_type="linux",
+ restart_policy="always",
+ image_registry_credentials=[
+ containerinstance.ImageRegistryCredentialArgs(
+ server=registry.login_server,
+ username=registry_username,
+ password=registry_password,
+ ),
+ ],
+ containers=[
+ containerinstance.ContainerArgs(
+ name=image_name,
+ image=image.image_name,
+ ports=[
+ containerinstance.ContainerPortArgs(
+ port=container_port,
+ protocol="tcp",
+ ),
+ ],
+ environment_variables=[
+ containerinstance.EnvironmentVariableArgs(
+ name="FLASK_RUN_PORT",
+ value=str(container_port),
+ ),
+ containerinstance.EnvironmentVariableArgs(
+ name="FLASK_RUN_HOST",
+ value="0.0.0.0",
+ ),
+ ],
+ resources=containerinstance.ResourceRequirementsArgs(
+ requests=containerinstance.ResourceRequestsArgs(
+ cpu=cpu,
+ memory_in_gb=memory,
+ ),
+ ),
+ ),
+ ],
+ ip_address=containerinstance.IpAddressArgs(
+ type=containerinstance.ContainerGroupIpAddressType.PUBLIC,
+ dns_name_label=dns_name,
+ ports=[
+ containerinstance.PortArgs(
+ port=container_port,
+ protocol="tcp",
+ ),
+ ],
+ ),
+ ),
+)
+
+# Export the service's IP address, hostname, and fully-qualified URL.
+pulumi.export("hostname", container_group.ip_address.apply(lambda addr: addr.fqdn))
+pulumi.export("ip", container_group.ip_address.apply(lambda addr: addr.ip))
+pulumi.export(
+ "url",
+ container_group.ip_address.apply(
+ lambda addr: f"http://{addr.fqdn}:{container_port}"
+ ),
+)
diff --git a/container-azure-python/app/Dockerfile b/container-azure-python/app/Dockerfile
new file mode 100644
index 000000000..2b2d4db60
--- /dev/null
+++ b/container-azure-python/app/Dockerfile
@@ -0,0 +1,9 @@
+FROM python:3.10-alpine
+WORKDIR /usr/src/app
+
+COPY requirements.txt ./
+RUN pip install -r requirements.txt
+
+COPY . .
+
+CMD ["flask", "run"]
diff --git a/container-azure-python/app/app.py b/container-azure-python/app/app.py
new file mode 100644
index 000000000..64a0c1dd4
--- /dev/null
+++ b/container-azure-python/app/app.py
@@ -0,0 +1,7 @@
+from flask import Flask
+
+app = Flask(__name__)
+
+@app.route("/")
+def index():
+ return { "message": "Hello, world!" }
diff --git a/container-azure-python/app/requirements.txt b/container-azure-python/app/requirements.txt
new file mode 100644
index 000000000..e3e9a71d9
--- /dev/null
+++ b/container-azure-python/app/requirements.txt
@@ -0,0 +1 @@
+Flask
diff --git a/container-azure-python/requirements.txt b/container-azure-python/requirements.txt
new file mode 100644
index 000000000..150701af6
--- /dev/null
+++ b/container-azure-python/requirements.txt
@@ -0,0 +1,4 @@
+pulumi>=3.0.0,<4.0.0
+pulumi-azure-native>=1.0.0,<2.0.0
+pulumi-random>=4.0.0,<5.0.0
+pulumi-docker>=3.0.0,<4.0.0
diff --git a/container-azure-typescript/.gitignore b/container-azure-typescript/.gitignore
new file mode 100644
index 000000000..c6958891d
--- /dev/null
+++ b/container-azure-typescript/.gitignore
@@ -0,0 +1,2 @@
+/bin/
+/node_modules/
diff --git a/container-azure-typescript/Pulumi.yaml b/container-azure-typescript/Pulumi.yaml
new file mode 100644
index 000000000..a46b063fc
--- /dev/null
+++ b/container-azure-typescript/Pulumi.yaml
@@ -0,0 +1,22 @@
+name: ${PROJECT}
+description: ${DESCRIPTION}
+runtime:
+ name: nodejs
+template:
+ description: A containerized service on Azure Container Instances
+ config:
+ azure-native:location:
+ description: The Azure region to deploy into
+ default: WestUS
+ appPath:
+ description: The path to the container application to deploy
+ default: app
+ containerPort:
+ description: The port to expose on the container
+ default: 80
+ cpu:
+ description: The number of CPU cores to allocate on the container
+ default: 1
+ memory:
+ description: The amount of memory, in GB, to allocate on the container
+ default: 2
diff --git a/container-azure-typescript/app/.dockerignore b/container-azure-typescript/app/.dockerignore
new file mode 100644
index 000000000..93f136199
--- /dev/null
+++ b/container-azure-typescript/app/.dockerignore
@@ -0,0 +1,2 @@
+node_modules
+npm-debug.log
diff --git a/container-azure-typescript/app/Dockerfile b/container-azure-typescript/app/Dockerfile
new file mode 100644
index 000000000..1d41c7e42
--- /dev/null
+++ b/container-azure-typescript/app/Dockerfile
@@ -0,0 +1,9 @@
+FROM node:16
+WORKDIR /usr/src/app
+
+COPY package*.json ./
+RUN npm install
+
+COPY . .
+
+CMD [ "node", "index.js" ]
diff --git a/container-azure-typescript/app/index.js b/container-azure-typescript/app/index.js
new file mode 100644
index 000000000..ccf779d1e
--- /dev/null
+++ b/container-azure-typescript/app/index.js
@@ -0,0 +1,13 @@
+"use strict";
+
+const express = require("express");
+const app = express();
+
+app.get("/", (req, res) => {
+ res.json({ message: "Hello, world!" });
+});
+
+const port = process.env.PORT;
+app.listen(port, () => {
+ console.log(`Listening on port ${port}`);
+});
diff --git a/container-azure-typescript/app/package.json b/container-azure-typescript/app/package.json
new file mode 100644
index 000000000..74a933871
--- /dev/null
+++ b/container-azure-typescript/app/package.json
@@ -0,0 +1,11 @@
+{
+ "name": "my-app",
+ "version": "0.1.0",
+ "main": "index.js",
+ "scripts": {
+ "start": "node index.js"
+ },
+ "dependencies": {
+ "express": "^4.16.1"
+ }
+}
diff --git a/container-azure-typescript/index.ts b/container-azure-typescript/index.ts
new file mode 100644
index 000000000..cc71fba1d
--- /dev/null
+++ b/container-azure-typescript/index.ts
@@ -0,0 +1,110 @@
+import * as pulumi from "@pulumi/pulumi";
+import * as resources from "@pulumi/azure-native/resources";
+import * as containerregistry from "@pulumi/azure-native/containerregistry";
+import * as containerinstance from "@pulumi/azure-native/containerinstance";
+import * as random from "@pulumi/random";
+import * as docker from "@pulumi/docker";
+
+// Import the program's configuration settings.
+const config = new pulumi.Config();
+const appPath = config.get("appPath") || "./app";
+const imageName = config.get("imageName") || "my-app";
+const imageTag = config.get("imageTag") || "latest";
+const containerPort = config.getNumber("containerPort") || 80;
+const cpu = config.getNumber("cpu") || 1;
+const memory = config.getNumber("memory") || 2;
+
+// Create a resource group for the container registry.
+const resourceGroup = new resources.ResourceGroup("resource-group");
+
+// Create a container registry.
+const registry = new containerregistry.Registry("registry", {
+ resourceGroupName: resourceGroup.name,
+ adminUserEnabled: true,
+ sku: {
+ name: containerregistry.SkuName.Basic,
+ },
+});
+
+// Fetch login credentials for the registry.
+const credentials = containerregistry.listRegistryCredentialsOutput({
+ resourceGroupName: resourceGroup.name,
+ registryName: registry.name,
+}).apply(creds => {
+ return {
+ username: creds.username!,
+ password: creds.passwords![0].value!,
+ };
+});
+
+// Create a container image for the service.
+const image = new docker.Image("image", {
+ imageName: pulumi.interpolate`${registry.loginServer}/${imageName}:${imageTag}`,
+ build: {
+ context: appPath,
+ },
+ registry: {
+ server: registry.loginServer,
+ username: credentials.username,
+ password: credentials.password,
+ },
+});
+
+// Use a random string to give the service a unique DNS name.
+const dnsName = new random.RandomString("dns-name", {
+ length: 8,
+ special: false,
+}).result.apply(result => `${imageName}-${result.toLowerCase()}`);
+
+// Create a container group for the service that makes it publicly accessible.
+const containerGroup = new containerinstance.ContainerGroup("container-group", {
+ resourceGroupName: resourceGroup.name,
+ osType: "linux",
+ restartPolicy: "always",
+ imageRegistryCredentials: [
+ {
+ server: registry.loginServer,
+ username: credentials.username,
+ password: credentials.password,
+ },
+ ],
+ containers: [
+ {
+ name: imageName,
+ image: image.imageName,
+ ports: [
+ {
+ port: containerPort,
+ protocol: "tcp",
+ },
+ ],
+ environmentVariables: [
+ {
+ name: "PORT",
+ value: containerPort.toString(),
+ },
+ ],
+ resources: {
+ requests: {
+ cpu: cpu,
+ memoryInGB: memory,
+ },
+ },
+ },
+ ],
+ ipAddress: {
+ type: containerinstance.ContainerGroupIpAddressType.Public,
+ dnsNameLabel: dnsName,
+ ports: [
+ {
+ port: containerPort,
+ protocol: "tcp",
+ },
+ ],
+ },
+});
+
+// Export the service's IP address, hostname, and fully-qualified URL.
+export const hostname = containerGroup.ipAddress.apply(addr => addr!.fqdn!);
+export const ip = containerGroup.ipAddress.apply(addr => addr!.ip!);
+export const url = containerGroup.ipAddress.apply(addr => `http://${addr!.fqdn!}:${containerPort}`);
diff --git a/container-azure-typescript/package.json b/container-azure-typescript/package.json
new file mode 100644
index 000000000..ec4ad0690
--- /dev/null
+++ b/container-azure-typescript/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "${PROJECT}",
+ "devDependencies": {
+ "@types/node": "^14"
+ },
+ "dependencies": {
+ "@pulumi/azure-native": "^1.0.0",
+ "@pulumi/docker": "^3.4.1",
+ "@pulumi/pulumi": "^3.0.0",
+ "@pulumi/random": "^4.8.2"
+ }
+}
diff --git a/container-azure-typescript/tsconfig.json b/container-azure-typescript/tsconfig.json
new file mode 100644
index 000000000..ab65afa61
--- /dev/null
+++ b/container-azure-typescript/tsconfig.json
@@ -0,0 +1,18 @@
+{
+ "compilerOptions": {
+ "strict": true,
+ "outDir": "bin",
+ "target": "es2016",
+ "module": "commonjs",
+ "moduleResolution": "node",
+ "sourceMap": true,
+ "experimentalDecorators": true,
+ "pretty": true,
+ "noFallthroughCasesInSwitch": true,
+ "noImplicitReturns": true,
+ "forceConsistentCasingInFileNames": true
+ },
+ "files": [
+ "index.ts"
+ ]
+}
diff --git a/metadata/groups/container-service-azure.yaml b/metadata/groups/container-service-azure.yaml
new file mode 100644
index 000000000..d1704a47b
--- /dev/null
+++ b/metadata/groups/container-service-azure.yaml
@@ -0,0 +1,11 @@
+name: Container Service on Azure
+kind: architecture
+parent: container-service
+slug: azure
+clouds:
+ - azure
+templates:
+ - container-azure-typescript
+ - container-azure-python
+ - container-azure-go
+ - container-azure-csharp