diff --git a/.github/workflows/UploadDockerImage.yml b/.github/workflows/UploadDockerImage.yml
new file mode 100644
index 000000000..ba0699df6
--- /dev/null
+++ b/.github/workflows/UploadDockerImage.yml
@@ -0,0 +1,44 @@
+# Pushes Docker images created from the deploy tool's Dockerfile templates to an internal ECR so that they can be scanned for security vulnerabilities.
+name: Upload Docker Images
+
+on:
+ # Manually trigger on specific branches
+ workflow_dispatch:
+ push:
+ branches:
+ - main
+
+permissions:
+ id-token: write
+
+jobs:
+ upload-docker-images:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Configure AWS Credentials
+ uses: aws-actions/configure-aws-credentials@8c3f20df09ac63af7b3ae3d7c91f105f857d8497 #v4
+ with:
+ aws-region: us-west-2
+ role-to-assume: ${{ secrets.DOCKER_IMAGE_UPLOADER_ROLE }}
+ role-duration-seconds: 1800
+
+ - name: Checkout Repository
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Setup .NET Core 6.0
+ uses: actions/setup-dotnet@v3
+ with:
+ dotnet-version: 6.0.x
+
+ - name: Restore dependencies
+ run: dotnet restore
+
+ - name: Build
+ run: dotnet build --no-restore
+
+ - name: Run Docker Image Uploader
+ run: |
+ cd ./test/AWS.Deploy.DockerImageUploader
+ dotnet run --project ./AWS.Deploy.DockerImageUploader.csproj
\ No newline at end of file
diff --git a/AWS.Deploy.sln b/AWS.Deploy.sln
index 6540ad553..0c96ba215 100644
--- a/AWS.Deploy.sln
+++ b/AWS.Deploy.sln
@@ -67,6 +67,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AWS.Deploy.DocGenerator", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AWS.Deploy.DocGenerator.UnitTests", "test\AWS.Deploy.DocGenerator.UnitTests\AWS.Deploy.DocGenerator.UnitTests.csproj", "{7E661545-7DFD-4FE3-A5F9-767FAE30DFFE}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AWS.Deploy.DockerImageUploader", "test\AWS.Deploy.DockerImageUploader\AWS.Deploy.DockerImageUploader.csproj", "{49A1C020-F4C8-4B28-A6B2-6AD3C8452E69}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -173,6 +175,10 @@ Global
{7E661545-7DFD-4FE3-A5F9-767FAE30DFFE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7E661545-7DFD-4FE3-A5F9-767FAE30DFFE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7E661545-7DFD-4FE3-A5F9-767FAE30DFFE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {49A1C020-F4C8-4B28-A6B2-6AD3C8452E69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {49A1C020-F4C8-4B28-A6B2-6AD3C8452E69}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {49A1C020-F4C8-4B28-A6B2-6AD3C8452E69}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {49A1C020-F4C8-4B28-A6B2-6AD3C8452E69}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -204,6 +210,7 @@ Global
{CEEBEC39-40E5-4A9B-878A-6EDB52B9B43E} = {C3A0C716-BDEA-4393-B223-AF8F8531522A}
{6D4BD0C2-C2A0-4AFB-BC22-623DD64A4F84} = {11C7056E-93C1-408B-BD87-5270595BBE0E}
{7E661545-7DFD-4FE3-A5F9-767FAE30DFFE} = {BD466B5C-D8B0-4069-98A9-6DC8F01FA757}
+ {49A1C020-F4C8-4B28-A6B2-6AD3C8452E69} = {BD466B5C-D8B0-4069-98A9-6DC8F01FA757}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5A4B2863-1763-4496-B122-651A38A4F5D7}
diff --git a/test/AWS.Deploy.DockerImageUploader/AWS.Deploy.DockerImageUploader.csproj b/test/AWS.Deploy.DockerImageUploader/AWS.Deploy.DockerImageUploader.csproj
new file mode 100644
index 000000000..91eb733db
--- /dev/null
+++ b/test/AWS.Deploy.DockerImageUploader/AWS.Deploy.DockerImageUploader.csproj
@@ -0,0 +1,13 @@
+
+
+
+ Exe
+ net6.0
+ enable
+
+
+
+
+
+
+
diff --git a/test/AWS.Deploy.DockerImageUploader/App.cs b/test/AWS.Deploy.DockerImageUploader/App.cs
new file mode 100644
index 000000000..ac4ee7aa6
--- /dev/null
+++ b/test/AWS.Deploy.DockerImageUploader/App.cs
@@ -0,0 +1,78 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+using AWS.Deploy.Common.IO;
+using AWS.Deploy.Common;
+using Microsoft.Extensions.DependencyInjection;
+using System.Reflection;
+using System.Collections.Generic;
+using System;
+using System.Threading.Tasks;
+using System.IO;
+
+namespace AWS.Deploy.DockerImageUploader
+{
+ ///
+ /// This serves as the dependency injection container for the console application.
+ ///
+ public class App
+ {
+ private readonly IFileManager _fileManager;
+ private readonly IDirectoryManager _directoryManager;
+ private readonly IProjectDefinitionParser _projectDefinitionParser;
+ private readonly CLI.App _deployToolCli;
+
+ private readonly List _testApps = new() { "WebApiNET6", "ConsoleAppTask" };
+
+ public App(IServiceProvider serviceProvider)
+ {
+ _projectDefinitionParser = serviceProvider.GetRequiredService();
+ _fileManager = serviceProvider.GetRequiredService();
+ _directoryManager = serviceProvider.GetRequiredService();
+ _deployToolCli = serviceProvider.GetRequiredService();
+ }
+
+ ///
+ /// Generates Dockerfiles for test applications using
+ /// the Dockerfile template.
+ /// It will then build and push the images to Amazon ECR where they are continuously scanned for security vulnerabilities.
+ ///
+ public async Task Run()
+ {
+ foreach (var testApp in _testApps)
+ {
+ var projectPath = ResolvePath(testApp);
+ await CreateImageAndPushToECR(projectPath);
+ }
+ }
+
+ private async Task CreateImageAndPushToECR(string projectPath)
+ {
+ var projectDefinition = await _projectDefinitionParser.Parse(projectPath);
+
+ var dockerEngine = new DockerEngine.DockerEngine(projectDefinition, _fileManager, _directoryManager);
+ dockerEngine.GenerateDockerFile();
+
+ var configFilePath = Path.Combine(projectPath, "DockerImageUploaderConfigFile.json");
+ var deployArgs = new[] { "deploy", "--project-path", projectPath, "--diagnostics", "--apply", configFilePath, "--silent" };
+ await _deployToolCli.Run(deployArgs);
+ }
+
+ private string ResolvePath(string projectName)
+ {
+ const string testDir = "test";
+ var testDirPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ while (testDirPath != null && !string.Equals(new DirectoryInfo(testDirPath).Name, testDir, StringComparison.OrdinalIgnoreCase))
+ {
+ testDirPath = Directory.GetParent(testDirPath)?.FullName;
+ }
+
+ if (string.IsNullOrEmpty(testDirPath))
+ {
+ throw new Exception($"Failed to find path to '{testDir}' directory.");
+ }
+
+ return Path.Combine(testDirPath, "..", "testapps", projectName);
+ }
+ }
+}
diff --git a/test/AWS.Deploy.DockerImageUploader/Program.cs b/test/AWS.Deploy.DockerImageUploader/Program.cs
new file mode 100644
index 000000000..695792c57
--- /dev/null
+++ b/test/AWS.Deploy.DockerImageUploader/Program.cs
@@ -0,0 +1,37 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+using Microsoft.Extensions.DependencyInjection;
+using AWS.Deploy.CLI.Extensions;
+using System.Threading.Tasks;
+using System;
+
+namespace AWS.Deploy.DockerImageUploader
+{
+ ///
+ /// This console app generates a docker file for a .NET console application and a web application via
+ /// the Dockerfile template.
+ /// It will then build and push the images to Amazon ECR where they are continuously scanned for security vulnerabilities.
+ ///
+ internal class Program
+ {
+ public static async Task Main(string[] args)
+ {
+ var serviceCollection = new ServiceCollection();
+
+ serviceCollection.AddCustomServices();
+ serviceCollection.AddSingleton();
+
+ var serviceProvider = serviceCollection.BuildServiceProvider();
+
+ var app = serviceProvider.GetService();
+ if (app == null)
+ {
+ throw new Exception("App dependencies aren't injected correctly." +
+ " Verify that all the required dependencies to instantiate DockerImageUploader are present.");
+ }
+
+ await app.Run();
+ }
+ }
+}
diff --git a/testapps/ConsoleAppTask/DockerImageUploaderConfigFile.json b/testapps/ConsoleAppTask/DockerImageUploaderConfigFile.json
new file mode 100644
index 000000000..fcc40ce0e
--- /dev/null
+++ b/testapps/ConsoleAppTask/DockerImageUploaderConfigFile.json
@@ -0,0 +1,7 @@
+{
+ "RecipeId": "PushContainerImageEcr",
+ "settings": {
+ "ImageTag": "latest",
+ "ECRRepositoryName": "deploytool-consoleapp"
+ }
+}
diff --git a/testapps/WebApiNET6/DockerImageUploaderConfigFile.json b/testapps/WebApiNET6/DockerImageUploaderConfigFile.json
new file mode 100644
index 000000000..51d813572
--- /dev/null
+++ b/testapps/WebApiNET6/DockerImageUploaderConfigFile.json
@@ -0,0 +1,7 @@
+{
+ "RecipeId": "PushContainerImageEcr",
+ "settings": {
+ "ImageTag": "latest",
+ "ECRRepositoryName": "deploytool-webapp"
+ }
+}