diff --git a/README.md b/README.md
index e6df4f9..77522a3 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,7 @@
-# resource-discovery-sandbox
\ No newline at end of file
+# Resource Discovery API Sandbox
+
+Sandbox to build a resource discovery API built on top of Azure Resource Graph.
+
+## License Information
+
+This is licensed under The MIT License (MIT). Which means that you can use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the web application. But you always need to state that Tom Kerkhove is the original author of this web application.
\ No newline at end of file
diff --git a/src/Promitor.ResourceDiscovery.Agent/Controllers/HealthController.cs b/src/Promitor.ResourceDiscovery.Agent/Controllers/HealthController.cs
new file mode 100644
index 0000000..0bcad9a
--- /dev/null
+++ b/src/Promitor.ResourceDiscovery.Agent/Controllers/HealthController.cs
@@ -0,0 +1,55 @@
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+using GuardNet;
+using Swashbuckle.AspNetCore.Filters;
+
+namespace Promitor.ResourceDiscovery.Agent.Controllers
+{
+ ///
+ /// API endpoint to check the health of the application.
+ ///
+ [ApiController]
+ [Route("api/v1/health")]
+ public class HealthController : ControllerBase
+ {
+ private readonly HealthCheckService _healthCheckService;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The service to provide the health of the API application.
+ public HealthController(HealthCheckService healthCheckService)
+ {
+ Guard.NotNull(healthCheckService, nameof(healthCheckService));
+
+ _healthCheckService = healthCheckService;
+ }
+
+ ///
+ /// Get Health
+ ///
+ /// Provides an indication about the health of the API.
+ /// API is healthy
+ /// API is unhealthy or in degraded state
+ [HttpGet(Name = "Health_Get")]
+ [ProducesResponseType(typeof(HealthReport), StatusCodes.Status200OK)]
+ [ProducesResponseType(typeof(HealthReport), StatusCodes.Status503ServiceUnavailable)]
+ [SwaggerResponseHeader(200, "RequestId", "string", "The header that has a request ID that uniquely identifies this operation call")]
+ [SwaggerResponseHeader(200, "X-Transaction-Id", "string", "The header that has the transaction ID is used to correlate multiple operation calls.")]
+ public async Task Get()
+ {
+ HealthReport healthReport = await _healthCheckService.CheckHealthAsync();
+
+ if (healthReport?.Status == HealthStatus.Healthy)
+ {
+ return Ok(healthReport);
+ }
+ else
+ {
+ return StatusCode(StatusCodes.Status503ServiceUnavailable, healthReport);
+ }
+ }
+ }
+}
diff --git a/src/Promitor.ResourceDiscovery.Agent/Dockerfile b/src/Promitor.ResourceDiscovery.Agent/Dockerfile
new file mode 100644
index 0000000..baae155
--- /dev/null
+++ b/src/Promitor.ResourceDiscovery.Agent/Dockerfile
@@ -0,0 +1,19 @@
+FROM mcr.microsoft.com/dotnet/core/aspnet:3.1.3-alpine3.10 AS base
+WORKDIR /app
+EXPOSE 80
+
+FROM mcr.microsoft.com/dotnet/core/sdk:3.1.201-alpine3.10 AS build
+WORKDIR /src
+COPY ["Promitor.ResourceDiscovery.Agent.csproj", ""]
+
+COPY . .
+WORKDIR "/src/."
+RUN dotnet build "Promitor.ResourceDiscovery.Agent.csproj" -c Release -o /app
+
+FROM build AS publish
+RUN dotnet publish "Promitor.ResourceDiscovery.Agent.csproj" -c Release -o /app
+
+FROM base AS final
+WORKDIR /app
+COPY --from=publish /app .
+ENTRYPOINT ["dotnet", "Promitor.ResourceDiscovery.Agent.dll"]
diff --git a/src/Promitor.ResourceDiscovery.Agent/Program.cs b/src/Promitor.ResourceDiscovery.Agent/Program.cs
new file mode 100644
index 0000000..7090414
--- /dev/null
+++ b/src/Promitor.ResourceDiscovery.Agent/Program.cs
@@ -0,0 +1,55 @@
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+
+namespace Promitor.ResourceDiscovery.Agent
+{
+ public class Program
+ {
+ public static int Main(string[] args)
+ {
+ CreateHostBuilder(args)
+ .Build()
+ .Run();
+
+ return 0;
+ }
+
+ public static IHostBuilder CreateHostBuilder(string[] args)
+ {
+ IConfiguration configuration = CreateConfiguration(args);
+ IHostBuilder webHostBuilder = CreateHostBuilder(args, configuration);
+
+ return webHostBuilder;
+ }
+
+ private static IConfiguration CreateConfiguration(string[] args)
+ {
+ IConfigurationRoot configuration =
+ new ConfigurationBuilder()
+ .AddCommandLine(args)
+ .AddEnvironmentVariables()
+ .Build();
+
+ return configuration;
+ }
+
+ private static IHostBuilder CreateHostBuilder(string[] args, IConfiguration configuration)
+ {
+ string httpEndpointUrl = "http://+:" + configuration["ARCUS_HTTP_PORT"];
+ IHostBuilder webHostBuilder =
+ Host.CreateDefaultBuilder(args)
+ .ConfigureAppConfiguration(configBuilder => configBuilder.AddConfiguration(configuration))
+ .ConfigureWebHostDefaults(webBuilder =>
+ {
+ webBuilder.ConfigureKestrel(kestrelServerOptions => kestrelServerOptions.AddServerHeader = false)
+ .UseUrls(httpEndpointUrl)
+ .ConfigureLogging(logging => logging.AddConsole())
+ .UseStartup();
+ });
+
+ return webHostBuilder;
+ }
+ }
+}
diff --git a/src/Promitor.ResourceDiscovery.Agent/Promitor.ResourceDiscovery.Agent.Open-Api.xml b/src/Promitor.ResourceDiscovery.Agent/Promitor.ResourceDiscovery.Agent.Open-Api.xml
new file mode 100644
index 0000000..24983d1
--- /dev/null
+++ b/src/Promitor.ResourceDiscovery.Agent/Promitor.ResourceDiscovery.Agent.Open-Api.xml
@@ -0,0 +1,37 @@
+
+
+
+ Promitor.ResourceDiscovery.Agent
+
+
+
+
+ API endpoint to check the health of the application.
+
+
+
+
+ Initializes a new instance of the class.
+
+ The service to provide the health of the API application.
+
+
+
+ Get Health
+
+ Provides an indication about the health of the API.
+ API is healthy
+ API is unhealthy or in degraded state
+
+
+
+ Initializes a new instance of the class.
+
+
+
+
+ Gets the configuration of key/value application properties.
+
+
+
+
diff --git a/src/Promitor.ResourceDiscovery.Agent/Promitor.ResourceDiscovery.Agent.csproj b/src/Promitor.ResourceDiscovery.Agent/Promitor.ResourceDiscovery.Agent.csproj
new file mode 100644
index 0000000..a810235
--- /dev/null
+++ b/src/Promitor.ResourceDiscovery.Agent/Promitor.ResourceDiscovery.Agent.csproj
@@ -0,0 +1,24 @@
+
+
+
+ netcoreapp3.1
+ true
+ Promitor.ResourceDiscovery.Agent.Open-Api.xml
+ Linux
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Promitor.ResourceDiscovery.Agent/Properties/launchSettings.json b/src/Promitor.ResourceDiscovery.Agent/Properties/launchSettings.json
new file mode 100644
index 0000000..0d6a250
--- /dev/null
+++ b/src/Promitor.ResourceDiscovery.Agent/Properties/launchSettings.json
@@ -0,0 +1,34 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:54553/",
+ "sslPort": 44315
+ }
+ },
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "Promitor.ResourceDiscovery.Agent": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "https://localhost:5001;http://localhost:5000"
+ },
+ "Docker": {
+ "commandName": "Docker",
+ "launchBrowser": true,
+ "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
+ "publishAllPorts": true,
+ "useSSL": false
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Promitor.ResourceDiscovery.Agent/Startup.cs b/src/Promitor.ResourceDiscovery.Agent/Startup.cs
new file mode 100644
index 0000000..65c3f53
--- /dev/null
+++ b/src/Promitor.ResourceDiscovery.Agent/Startup.cs
@@ -0,0 +1,122 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Text.Json.Serialization;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Formatters;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.OpenApi.Models;
+using Swashbuckle.AspNetCore.Filters;
+using Arcus.WebApi.Correlation;
+
+namespace Promitor.ResourceDiscovery.Agent
+{
+ public class Startup
+ {
+ private const string ApiName = "Promitor - Resource Discovery API";
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public Startup(IConfiguration configuration)
+ {
+ Configuration = configuration;
+ }
+
+ ///
+ /// Gets the configuration of key/value application properties.
+ ///
+ public IConfiguration Configuration { get; }
+
+ // This method gets called by the runtime. Use this method to add services to the container.
+ // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services.AddRouting(options =>
+ {
+ options.LowercaseUrls = true;
+ options.LowercaseQueryStrings = true;
+ });
+ services.AddControllers(options =>
+ {
+ options.ReturnHttpNotAcceptable = true;
+ options.RespectBrowserAcceptHeader = true;
+
+ RestrictToJsonContentType(options);
+ AddEnumAsStringRepresentation(options);
+
+ });
+
+ services.AddHealthChecks();
+ services.AddCorrelation();
+
+#if DEBUG
+ var openApiInformation = new OpenApiInfo
+ {
+ Title = ApiName,
+ Version = "v1"
+ };
+
+ services.AddSwaggerGen(swaggerGenerationOptions =>
+ {
+ swaggerGenerationOptions.SwaggerDoc("v1", openApiInformation);
+ swaggerGenerationOptions.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "Promitor.ResourceDiscovery.Agent.Open-Api.xml"));
+
+ swaggerGenerationOptions.OperationFilter("X-Transaction-Id", "Transaction ID is used to correlate multiple operation calls. A new transaction ID will be generated if not specified.", false);
+ swaggerGenerationOptions.OperationFilter();
+ });
+#endif
+ }
+
+ private static void RestrictToJsonContentType(MvcOptions options)
+ {
+ var allButJsonInputFormatters = options.InputFormatters.Where(formatter => !(formatter is SystemTextJsonInputFormatter));
+ foreach (IInputFormatter inputFormatter in allButJsonInputFormatters)
+ {
+ options.InputFormatters.Remove(inputFormatter);
+ }
+
+ // Removing for text/plain, see https://docs.microsoft.com/en-us/aspnet/core/web-api/advanced/formatting?view=aspnetcore-3.0#special-case-formatters
+ options.OutputFormatters.RemoveType();
+ }
+
+ private static void AddEnumAsStringRepresentation(MvcOptions options)
+ {
+ var onlyJsonInputFormatters = options.InputFormatters.OfType();
+ foreach (SystemTextJsonInputFormatter inputFormatter in onlyJsonInputFormatters)
+ {
+ inputFormatter.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
+ }
+
+ var onlyJsonOutputFormatters = options.OutputFormatters.OfType();
+ foreach (SystemTextJsonOutputFormatter outputFormatter in onlyJsonOutputFormatters)
+ {
+ outputFormatter.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
+ }
+ }
+
+ // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+ {
+ app.UseMiddleware();
+ app.UseCorrelation();
+ app.UseRouting();
+
+ app.UseSwagger(swaggerOptions =>
+ {
+ swaggerOptions.RouteTemplate = "api/{documentName}/docs.json";
+ });
+ app.UseSwaggerUI(swaggerUiOptions =>
+ {
+ swaggerUiOptions.SwaggerEndpoint("/api/v1/docs.json", ApiName);
+ swaggerUiOptions.RoutePrefix = "api/docs";
+ swaggerUiOptions.DocumentTitle = ApiName;
+ });
+ app.UseEndpoints(endpoints => endpoints.MapControllers());
+
+ }
+ }
+}
diff --git a/src/Promitor.ResourceDiscovery.sln b/src/Promitor.ResourceDiscovery.sln
new file mode 100644
index 0000000..9163e65
--- /dev/null
+++ b/src/Promitor.ResourceDiscovery.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29609.76
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Promitor.ResourceDiscovery.Agent", "Promitor.ResourceDiscovery.Agent\Promitor.ResourceDiscovery.Agent.csproj", "{DE2E7C21-E261-4A78-8C63-7EEBD2CB3354}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {DE2E7C21-E261-4A78-8C63-7EEBD2CB3354}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DE2E7C21-E261-4A78-8C63-7EEBD2CB3354}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DE2E7C21-E261-4A78-8C63-7EEBD2CB3354}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DE2E7C21-E261-4A78-8C63-7EEBD2CB3354}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {E9FC4DEA-2E5E-40A2-9399-B6AC11ECEC12}
+ EndGlobalSection
+EndGlobal