diff --git a/src/AzureIntegration/.gitignore b/src/AzureIntegration/.gitignore
new file mode 100644
index 0000000000..5c05ff5f40
--- /dev/null
+++ b/src/AzureIntegration/.gitignore
@@ -0,0 +1,34 @@
+[Oo]bj/
+[Bb]in/
+TestResults/
+.nuget/
+_ReSharper.*/
+packages/
+artifacts/
+PublishProfiles/
+*.user
+*.suo
+*.cache
+*.docstates
+_ReSharper.*
+nuget.exe
+*net45.csproj
+*net451.csproj
+*k10.csproj
+*.psess
+*.vsp
+*.pidb
+*.userprefs
+*DS_Store
+*.ncrunchsolution
+*.*sdf
+*.ipch
+*.sln.ide
+project.lock.json
+.vs
+.build/
+.testPublish/
+global.json
+msbuild.binlog
+.test-dotnet/
+.deps/
\ No newline at end of file
diff --git a/src/AzureIntegration/AzureIntegration.sln b/src/AzureIntegration/AzureIntegration.sln
new file mode 100644
index 0000000000..cc9ac83c23
--- /dev/null
+++ b/src/AzureIntegration/AzureIntegration.sln
@@ -0,0 +1,117 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.27016.1
+MinimumVisualStudioVersion = 15.0.26730.03
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.AzureAppServicesIntegration", "src\Microsoft.AspNetCore.AzureAppServicesIntegration\Microsoft.AspNetCore.AzureAppServicesIntegration.csproj", "{5916BEB5-0969-469B-976C-A392E015DFAC}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{FF9B744E-6C59-40CC-9E41-9D2EBD292435}"
+ ProjectSection(SolutionItems) = preProject
+ src\Directory.Build.props = src\Directory.Build.props
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2FFE2B87-BF8A-4B38-ADAB-2FE2F9BC4A7C}"
+ ProjectSection(SolutionItems) = preProject
+ build\dependencies.props = build\dependencies.props
+ Directory.Build.props = Directory.Build.props
+ Directory.Build.targets = Directory.Build.targets
+ NuGet.config = NuGet.config
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AzureAppServicesSample", "sample\AzureAppServicesSample\AzureAppServicesSample.csproj", "{05A4D308-B162-4194-BC5E-88CCB4DBD318}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{37237C93-6855-40D9-9E60-418B093EF49A}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{CD650B4B-81C2-4A44-AEF2-A251A877C1F0}"
+ ProjectSection(SolutionItems) = preProject
+ test\Directory.Build.props = test\Directory.Build.props
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.AzureAppServicesIntegration.Tests", "test\Microsoft.AspNetCore.AzureAppServicesIntegration.Tests\Microsoft.AspNetCore.AzureAppServicesIntegration.Tests.csproj", "{9BA1B692-B313-4E22-A864-F0ADBBE3C3FA}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.AzureAppServices.HostingStartup", "src\Microsoft.AspNetCore.AzureAppServices.HostingStartup\Microsoft.AspNetCore.AzureAppServices.HostingStartup.csproj", "{AC023B45-7995-4D4A-8108-E512AE8E5734}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AzureAppServicesHostingStartupSample", "sample\AzureAppServicesHostingStartupSample\AzureAppServicesHostingStartupSample.csproj", "{939EA897-CA31-4F2E-BA51-22B570B64671}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Web.Xdt.Extensions", "src\Microsoft.Web.Xdt.Extensions\Microsoft.Web.Xdt.Extensions.csproj", "{9B22E525-FEC9-4C7C-9F9C-598C15BD0250}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.AzureAppServices.SiteExtension", "extensions\Microsoft.AspNetCore.AzureAppServices.SiteExtension\Microsoft.AspNetCore.AzureAppServices.SiteExtension.csproj", "{1CE2D76B-39E6-46C0-8F6F-C63E370955A9}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Web.Xdt.Extensions.Tests", "test\Microsoft.Web.Xdt.Extensions.Tests\Microsoft.Web.Xdt.Extensions.Tests.csproj", "{809AEE05-1B28-4E31-8959-776B249BD725}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.ApplicationModelDetection", "src\Microsoft.Extensions.ApplicationModelDetection\Microsoft.Extensions.ApplicationModelDetection.csproj", "{F0CABFE8-A5B1-487B-A451-A486D26742D3}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.ApplicationModelDetection.Tests", "test\Microsoft.Extensions.ApplicationModelDetection.Tests\Microsoft.Extensions.ApplicationModelDetection.Tests.csproj", "{15664836-2B94-4D2D-AC18-6DED01FCCCBD}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Runtime.SiteExtension", "extensions\Microsoft.AspNetCore.Runtime.SiteExtension\Microsoft.AspNetCore.Runtime.SiteExtension.csproj", "{E1E9BC7A-6951-4B60-8DFB-DBB9AC3CDEB0}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {5916BEB5-0969-469B-976C-A392E015DFAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5916BEB5-0969-469B-976C-A392E015DFAC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5916BEB5-0969-469B-976C-A392E015DFAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5916BEB5-0969-469B-976C-A392E015DFAC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {05A4D308-B162-4194-BC5E-88CCB4DBD318}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {05A4D308-B162-4194-BC5E-88CCB4DBD318}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {05A4D308-B162-4194-BC5E-88CCB4DBD318}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {05A4D308-B162-4194-BC5E-88CCB4DBD318}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9BA1B692-B313-4E22-A864-F0ADBBE3C3FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9BA1B692-B313-4E22-A864-F0ADBBE3C3FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9BA1B692-B313-4E22-A864-F0ADBBE3C3FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9BA1B692-B313-4E22-A864-F0ADBBE3C3FA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AC023B45-7995-4D4A-8108-E512AE8E5734}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AC023B45-7995-4D4A-8108-E512AE8E5734}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AC023B45-7995-4D4A-8108-E512AE8E5734}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AC023B45-7995-4D4A-8108-E512AE8E5734}.Release|Any CPU.Build.0 = Release|Any CPU
+ {939EA897-CA31-4F2E-BA51-22B570B64671}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {939EA897-CA31-4F2E-BA51-22B570B64671}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {939EA897-CA31-4F2E-BA51-22B570B64671}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {939EA897-CA31-4F2E-BA51-22B570B64671}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9B22E525-FEC9-4C7C-9F9C-598C15BD0250}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9B22E525-FEC9-4C7C-9F9C-598C15BD0250}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9B22E525-FEC9-4C7C-9F9C-598C15BD0250}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9B22E525-FEC9-4C7C-9F9C-598C15BD0250}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1CE2D76B-39E6-46C0-8F6F-C63E370955A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1CE2D76B-39E6-46C0-8F6F-C63E370955A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1CE2D76B-39E6-46C0-8F6F-C63E370955A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1CE2D76B-39E6-46C0-8F6F-C63E370955A9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {809AEE05-1B28-4E31-8959-776B249BD725}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {809AEE05-1B28-4E31-8959-776B249BD725}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {809AEE05-1B28-4E31-8959-776B249BD725}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {809AEE05-1B28-4E31-8959-776B249BD725}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F0CABFE8-A5B1-487B-A451-A486D26742D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F0CABFE8-A5B1-487B-A451-A486D26742D3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F0CABFE8-A5B1-487B-A451-A486D26742D3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F0CABFE8-A5B1-487B-A451-A486D26742D3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {15664836-2B94-4D2D-AC18-6DED01FCCCBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {15664836-2B94-4D2D-AC18-6DED01FCCCBD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {15664836-2B94-4D2D-AC18-6DED01FCCCBD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {15664836-2B94-4D2D-AC18-6DED01FCCCBD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E1E9BC7A-6951-4B60-8DFB-DBB9AC3CDEB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E1E9BC7A-6951-4B60-8DFB-DBB9AC3CDEB0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E1E9BC7A-6951-4B60-8DFB-DBB9AC3CDEB0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E1E9BC7A-6951-4B60-8DFB-DBB9AC3CDEB0}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {5916BEB5-0969-469B-976C-A392E015DFAC} = {FF9B744E-6C59-40CC-9E41-9D2EBD292435}
+ {05A4D308-B162-4194-BC5E-88CCB4DBD318} = {37237C93-6855-40D9-9E60-418B093EF49A}
+ {9BA1B692-B313-4E22-A864-F0ADBBE3C3FA} = {CD650B4B-81C2-4A44-AEF2-A251A877C1F0}
+ {AC023B45-7995-4D4A-8108-E512AE8E5734} = {FF9B744E-6C59-40CC-9E41-9D2EBD292435}
+ {939EA897-CA31-4F2E-BA51-22B570B64671} = {37237C93-6855-40D9-9E60-418B093EF49A}
+ {9B22E525-FEC9-4C7C-9F9C-598C15BD0250} = {FF9B744E-6C59-40CC-9E41-9D2EBD292435}
+ {1CE2D76B-39E6-46C0-8F6F-C63E370955A9} = {FF9B744E-6C59-40CC-9E41-9D2EBD292435}
+ {809AEE05-1B28-4E31-8959-776B249BD725} = {CD650B4B-81C2-4A44-AEF2-A251A877C1F0}
+ {F0CABFE8-A5B1-487B-A451-A486D26742D3} = {FF9B744E-6C59-40CC-9E41-9D2EBD292435}
+ {15664836-2B94-4D2D-AC18-6DED01FCCCBD} = {CD650B4B-81C2-4A44-AEF2-A251A877C1F0}
+ {E1E9BC7A-6951-4B60-8DFB-DBB9AC3CDEB0} = {FF9B744E-6C59-40CC-9E41-9D2EBD292435}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {5743DFE7-1AA5-439D-84AE-A480EA389927}
+ EndGlobalSection
+EndGlobal
diff --git a/src/AzureIntegration/Directory.Build.props b/src/AzureIntegration/Directory.Build.props
new file mode 100644
index 0000000000..dcab7b1a0f
--- /dev/null
+++ b/src/AzureIntegration/Directory.Build.props
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+ Microsoft ASP.NET Core
+ https://github.com/aspnet/AzureIntegration
+ git
+ $(MSBuildThisFileDirectory)
+ $(MSBuildThisFileDirectory)build\Key.snk
+ true
+ true
+
+
+
diff --git a/src/AzureIntegration/Directory.Build.targets b/src/AzureIntegration/Directory.Build.targets
new file mode 100644
index 0000000000..7e3f8df92e
--- /dev/null
+++ b/src/AzureIntegration/Directory.Build.targets
@@ -0,0 +1,6 @@
+
+
+ $(MicrosoftNETCoreAppPackageVersion)
+ $(NETStandardLibrary20PackageVersion)
+
+
diff --git a/src/AzureIntegration/NuGetPackageVerifier.json b/src/AzureIntegration/NuGetPackageVerifier.json
new file mode 100644
index 0000000000..cf7178529c
--- /dev/null
+++ b/src/AzureIntegration/NuGetPackageVerifier.json
@@ -0,0 +1,16 @@
+{
+ "adx": { // Packages written by the ADX team and that ship on NuGet.org
+ "rules": [
+ "AdxVerificationCompositeRule"
+ ],
+ "packages": {
+ "Microsoft.AspNetCore.AzureAppServicesIntegration": { },
+ "Microsoft.Extensions.ApplicationModelDetection": { }
+ }
+ },
+ "Default": { // Rules to run for packages not listed in any other set.
+ "rules": [
+ "DefaultCompositeRule"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/src/AzureIntegration/README.md b/src/AzureIntegration/README.md
new file mode 100644
index 0000000000..1eeece66ef
--- /dev/null
+++ b/src/AzureIntegration/README.md
@@ -0,0 +1,27 @@
+AzureIntegration
+===
+
+Features that integrate ASP.NET Core with Azure.
+
+This project is part of ASP.NET Core. You can find samples, documentation and getting started instructions for ASP.NET Core at the [Home](https://github.com/aspnet/home) repo.
+
+
+SiteExtensions
+===
+
+To install a nightly preview of the ASP.NET Core runtime site extension for testing purposes:
+1. In the Azure portal select App Services -> your site -> Application settings
+1. Set `SCM_SITEEXTENSIONS_FEED_URL` application setting to `https://dotnet.myget.org/F/aspnetcore-dev/`
+1. Go to `DEVELOPMENT TOOLS` -> `Advanced Tools` -> `Site extensions` -> `Gallery`
+1. Enter `AspNetCoreRuntime` into `Search` box and click `Search`
+1. Click `+` to install site extension and wait untill installation animation finishes
+1. `Extensions` tab should now show newly installed site extension
+1. Click `Restart site` on the right side of the page when installation finishes (this would only restart Kudu site, not the main one)
+1. Restart site in `Overview` tab of `App service`
+
+
+To update ASP.NET Core runtime site extension:
+1. Stop site in `Overview` tab of `App service`
+1. Go to `DEVELOPMENT TOOLS` -> `Advanced Tools` -> `Site extensions`
+1. Click update on site extension
+1. Start site in `Overview` tab of `App service`
\ No newline at end of file
diff --git a/src/AzureIntegration/build/Key.snk b/src/AzureIntegration/build/Key.snk
new file mode 100644
index 0000000000..e10e4889c1
Binary files /dev/null and b/src/AzureIntegration/build/Key.snk differ
diff --git a/src/AzureIntegration/build/dependencies.props b/src/AzureIntegration/build/dependencies.props
new file mode 100644
index 0000000000..3c2678cb58
--- /dev/null
+++ b/src/AzureIntegration/build/dependencies.props
@@ -0,0 +1,47 @@
+
+
+ $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
+
+
+ 3.0.0-build-20181114.5
+ 3.0.0-build-20181114.5
+ 3.0.0-alpha1-10742
+ 3.0.0-alpha1-10742
+ 3.0.0-alpha1-10742
+ 3.0.0-alpha1-10742
+ 3.0.0-alpha1-10742
+ 3.0.0-alpha1-10742
+ 3.0.0-alpha1-10742
+ 3.0.0-alpha1-10742
+ 3.0.0-alpha1-10742
+ 0.7.0-alpha1-10742
+ 3.0.0-alpha1-10742
+ 3.0.0-alpha1-10742
+ 1.1.3
+ 1.0.3
+ 3.0.0-alpha1-10670
+ 3.0.0-preview-181113-11
+ 3.0.0-preview-181113-11
+ 3.0.0-preview-181113-11
+ 3.0.0-preview-181113-11
+ 3.0.0-preview-181113-11
+ 3.0.0-preview-181113-11
+ 3.0.0-preview-181113-11
+ 3.0.0-preview-181113-11
+ 3.0.0-preview-181113-11
+ 3.0.0-preview-181113-11
+ 3.0.0-preview1-26907-05
+ 3.0.0-alpha1-10742
+ 15.6.1
+ 1.4.0
+ 4.10.0
+ 2.0.3
+ 11.0.2
+ 1.7.0-preview1-26907-04
+ 8.1.4
+ 2.3.1
+ 2.4.0
+
+
+
+
diff --git a/src/AzureIntegration/build/hostingstartup.targets b/src/AzureIntegration/build/hostingstartup.targets
new file mode 100644
index 0000000000..2e8dd15047
--- /dev/null
+++ b/src/AzureIntegration/build/hostingstartup.targets
@@ -0,0 +1,11 @@
+
+
+
+ <_PackageFiles Include="$(ProjectDepsFilePath)">
+ lib\$(TargetFramework)
+ false
+ Content
+
+
+
+
diff --git a/src/AzureIntegration/build/repo.props b/src/AzureIntegration/build/repo.props
new file mode 100644
index 0000000000..a8505ea0f4
--- /dev/null
+++ b/src/AzureIntegration/build/repo.props
@@ -0,0 +1,35 @@
+
+
+
+
+ $(RepositoryRoot)test\Microsoft.AspNetCore.AzureAppServices.FunctionalTests\Microsoft.AspNetCore.AzureAppServices.FunctionalTests.csproj
+
+
+ Internal.AspNetCore.Universe.Lineup
+ https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/AzureIntegration/build/repo.targets b/src/AzureIntegration/build/repo.targets
new file mode 100644
index 0000000000..199837a956
--- /dev/null
+++ b/src/AzureIntegration/build/repo.targets
@@ -0,0 +1,94 @@
+
+
+
+ True
+ <_SdkVersion>$(SITE_EXTENSION_SDK_VERSION)
+ <_SdkVersion Condition="'$(_SdkVersion)' == ''">$([System.IO.Path]::GetFileName($([System.IO.Path]::GetDirectoryName('$(MSBuildExtensionsPath)'))))
+ <_SdkFeed>$(SITE_EXTENSION_SDK_FEED)
+ <_SdkFeed Condition="'$(_SdkFeed)' == ''">$(DefaultDotNetAssetFeed)
+ $(RepositoryRoot).test-dotnet\
+ $(RepositoryRoot)artifacts\apps
+ $(TestDotNetPath)extension\$(SiteExtensionArch)\
+ $(RepositoryRoot)artifacts\build
+ $(RepositoryRoot)\test\Microsoft.AspNetCore.AzureAppServices.FunctionalTests\
+ https://dotnet.myget.org/F/aspnetcore-dev/
+ $(MicrosoftNETCoreAppPackageVersion)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/AzureIntegration/build/sources.props b/src/AzureIntegration/build/sources.props
new file mode 100644
index 0000000000..9215df9751
--- /dev/null
+++ b/src/AzureIntegration/build/sources.props
@@ -0,0 +1,17 @@
+
+
+
+
+ $(DotNetRestoreSources)
+
+ $(RestoreSources);
+ https://dotnet.myget.org/F/dotnet-core/api/v3/index.json;
+ https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json;
+ https://dotnet.myget.org/F/aspnetcore-tools/api/v3/index.json;
+
+
+ $(RestoreSources);
+ https://api.nuget.org/v3/index.json;
+
+
+
diff --git a/src/AzureIntegration/extensions/Directory.Build.props b/src/AzureIntegration/extensions/Directory.Build.props
new file mode 100644
index 0000000000..d333f1a2b7
--- /dev/null
+++ b/src/AzureIntegration/extensions/Directory.Build.props
@@ -0,0 +1,23 @@
+
+
+
+ net461
+ false
+ AzureSiteExtension
+ true
+ false
+ false
+ false
+ content
+
+ $(NoWarn);NU5119
+
+
+
+
+
+
diff --git a/src/AzureIntegration/extensions/Microsoft.AspNetCore.AzureAppServices.SiteExtension/Microsoft.AspNetCore.AzureAppServices.SiteExtension.csproj b/src/AzureIntegration/extensions/Microsoft.AspNetCore.AzureAppServices.SiteExtension/Microsoft.AspNetCore.AzureAppServices.SiteExtension.csproj
new file mode 100644
index 0000000000..00f21b9bae
--- /dev/null
+++ b/src/AzureIntegration/extensions/Microsoft.AspNetCore.AzureAppServices.SiteExtension/Microsoft.AspNetCore.AzureAppServices.SiteExtension.csproj
@@ -0,0 +1,21 @@
+
+
+
+ $(VersionPrefix.Substring(0, $(VersionPrefix.LastIndexOf('.'))))
+ ASP.NET Core Extensions
+ This extension enables additional functionality for ASP.NET Core on Azure WebSites, such as enabling Azure logging.
+ netcoreapp3.0
+ false
+ aspnet;logging;aspnetcore;AzureSiteExtension;keyvault;configuration;dataprotection
+ content
+ $(MicrosoftNETCoreAppPackageVersion)
+ Microsoft.AspNetCore.AzureAppServices.SiteExtension.$(TrimmedVersion)
+
+
+
+
+
+
+
+
+
diff --git a/src/AzureIntegration/extensions/Microsoft.AspNetCore.Runtime.SiteExtension/Microsoft.AspNetCore.Runtime.SiteExtension.csproj b/src/AzureIntegration/extensions/Microsoft.AspNetCore.Runtime.SiteExtension/Microsoft.AspNetCore.Runtime.SiteExtension.csproj
new file mode 100644
index 0000000000..6ff0419a6f
--- /dev/null
+++ b/src/AzureIntegration/extensions/Microsoft.AspNetCore.Runtime.SiteExtension/Microsoft.AspNetCore.Runtime.SiteExtension.csproj
@@ -0,0 +1,36 @@
+
+
+
+ $(VersionPrefix.Substring(0, $(VersionPrefix.LastIndexOf('.'))))
+ ASP.NET Core $(TrimmedVersion) ($(RuntimeArch)) Runtime
+ This site extension installs Microsoft.AspNetCore.All, Microsoft.AspNetCore.App and Microsoft.NetCore.App shared runtimes.
+ aspnetcore;AzureSiteExtension
+ AspNetCoreRuntime.$(TrimmedVersion).$(RuntimeArch)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/AzureIntegration/extensions/Microsoft.AspNetCore.Runtime.SiteExtension/applicationHost.xdt b/src/AzureIntegration/extensions/Microsoft.AspNetCore.Runtime.SiteExtension/applicationHost.xdt
new file mode 100644
index 0000000000..159bc8d646
--- /dev/null
+++ b/src/AzureIntegration/extensions/Microsoft.AspNetCore.Runtime.SiteExtension/applicationHost.xdt
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/AzureIntegration/extensions/Microsoft.AspNetCore.Runtime.SiteExtension/install.cmd b/src/AzureIntegration/extensions/Microsoft.AspNetCore.Runtime.SiteExtension/install.cmd
new file mode 100644
index 0000000000..bdca3efa75
--- /dev/null
+++ b/src/AzureIntegration/extensions/Microsoft.AspNetCore.Runtime.SiteExtension/install.cmd
@@ -0,0 +1,16 @@
+FOR /R %%x IN (*.nupkg_) DO REN "%%x" "*.nupkg"
+
+
+SET DOTNET=D:\Program Files (x86)\dotnet
+SET RUNTIMES=%DOTNET%\shared\Microsoft.NETCore.App
+
+IF "%ASPNETCORE_COPY_EXISTING_RUNTIMES%" NEQ "1" EXIT /b 0
+
+robocopy "%DOTNET%" "." /E /XC /XN /XO /NFL /NDL ^
+ /XD "%DOTNET%\sdk" ^
+ /XD "%RUNTIMES%\1.0.8" ^
+ /XD "%RUNTIMES%\1.1.5" ^
+ /XD "%RUNTIMES%\2.0.3"
+
+IF %errorlevel% geq 8 EXIT /b 1
+EXIT /b 0
\ No newline at end of file
diff --git a/src/AzureIntegration/extensions/Microsoft.AspNetCore.Runtime.SiteExtension/scmApplicationHost.xdt b/src/AzureIntegration/extensions/Microsoft.AspNetCore.Runtime.SiteExtension/scmApplicationHost.xdt
new file mode 100644
index 0000000000..a8dd367f9f
--- /dev/null
+++ b/src/AzureIntegration/extensions/Microsoft.AspNetCore.Runtime.SiteExtension/scmApplicationHost.xdt
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/AzureIntegration/sample/AzureAppServicesHostingStartupSample/AzureAppServicesHostingStartupSample.csproj b/src/AzureIntegration/sample/AzureAppServicesHostingStartupSample/AzureAppServicesHostingStartupSample.csproj
new file mode 100644
index 0000000000..a2f447ed5e
--- /dev/null
+++ b/src/AzureIntegration/sample/AzureAppServicesHostingStartupSample/AzureAppServicesHostingStartupSample.csproj
@@ -0,0 +1,18 @@
+
+
+
+ netcoreapp3.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/AzureIntegration/sample/AzureAppServicesHostingStartupSample/Properties/launchSettings.json b/src/AzureIntegration/sample/AzureAppServicesHostingStartupSample/Properties/launchSettings.json
new file mode 100644
index 0000000000..731d8620e9
--- /dev/null
+++ b/src/AzureIntegration/sample/AzureAppServicesHostingStartupSample/Properties/launchSettings.json
@@ -0,0 +1,24 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:22071/",
+ "sslPort": 0
+ }
+ },
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.AzureAppServices.HostingStartup;Microsoft.AspNetCore.Server.IISIntegration"
+ }
+ },
+ "AzureAppServicesHostingStartupSample": {
+ "commandName": "IISExpress",
+ "launchBrowser": true
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AzureIntegration/sample/AzureAppServicesHostingStartupSample/Startup.cs b/src/AzureIntegration/sample/AzureAppServicesHostingStartupSample/Startup.cs
new file mode 100644
index 0000000000..1a91822bc1
--- /dev/null
+++ b/src/AzureIntegration/sample/AzureAppServicesHostingStartupSample/Startup.cs
@@ -0,0 +1,76 @@
+using System;
+using System.Linq;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
+
+namespace IISSample
+{
+ public class Startup
+ {
+ public void Configure(IApplicationBuilder app, ILoggerFactory loggerfactory)
+ {
+ var logger = loggerfactory.CreateLogger("Requests");
+
+ app.Run(async (context) =>
+ {
+ logger.LogDebug("Received request: " + context.Request.Method + " " + context.Request.Path);
+
+ context.Response.ContentType = "text/plain";
+ await context.Response.WriteAsync("Hello World - " + DateTimeOffset.Now + Environment.NewLine);
+ await context.Response.WriteAsync(Environment.NewLine);
+
+ await context.Response.WriteAsync("Address:" + Environment.NewLine);
+ await context.Response.WriteAsync("Scheme: " + context.Request.Scheme + Environment.NewLine);
+ await context.Response.WriteAsync("Host: " + context.Request.Headers["Host"] + Environment.NewLine);
+ await context.Response.WriteAsync("PathBase: " + context.Request.PathBase.Value + Environment.NewLine);
+ await context.Response.WriteAsync("Path: " + context.Request.Path.Value + Environment.NewLine);
+ await context.Response.WriteAsync("Query: " + context.Request.QueryString.Value + Environment.NewLine);
+ await context.Response.WriteAsync(Environment.NewLine);
+
+ await context.Response.WriteAsync("Connection:" + Environment.NewLine);
+ await context.Response.WriteAsync("RemoteIp: " + context.Connection.RemoteIpAddress + Environment.NewLine);
+ await context.Response.WriteAsync("RemotePort: " + context.Connection.RemotePort + Environment.NewLine);
+ await context.Response.WriteAsync("LocalIp: " + context.Connection.LocalIpAddress + Environment.NewLine);
+ await context.Response.WriteAsync("LocalPort: " + context.Connection.LocalPort + Environment.NewLine);
+ await context.Response.WriteAsync("ClientCert: " + context.Connection.ClientCertificate + Environment.NewLine);
+ await context.Response.WriteAsync(Environment.NewLine);
+
+ await context.Response.WriteAsync("User: " + context.User.Identity.Name + Environment.NewLine);
+ await context.Response.WriteAsync(Environment.NewLine);
+
+ await context.Response.WriteAsync("Headers:" + Environment.NewLine);
+ foreach (var header in context.Request.Headers)
+ {
+ await context.Response.WriteAsync(header.Key + ": " + header.Value + Environment.NewLine);
+ }
+ await context.Response.WriteAsync(Environment.NewLine);
+
+ await context.Response.WriteAsync("Environment Variables:" + Environment.NewLine);
+ var vars = Environment.GetEnvironmentVariables();
+ foreach (var key in vars.Keys.Cast().OrderBy(key => key, StringComparer.OrdinalIgnoreCase))
+ {
+ var value = vars[key];
+ await context.Response.WriteAsync(key + ": " + value + Environment.NewLine);
+ }
+ await context.Response.WriteAsync(Environment.NewLine);
+ });
+ }
+
+ public static void Main(string[] args)
+ {
+ var host = new WebHostBuilder()
+ .ConfigureLogging(factory =>
+ {
+ factory.AddConsole();
+ })
+ .UseKestrel()
+ .UseStartup()
+ .Build();
+
+ host.Run();
+ }
+ }
+}
+
diff --git a/src/AzureIntegration/sample/AzureAppServicesSample/AzureAppServicesSample.csproj b/src/AzureIntegration/sample/AzureAppServicesSample/AzureAppServicesSample.csproj
new file mode 100644
index 0000000000..4b03ef5612
--- /dev/null
+++ b/src/AzureIntegration/sample/AzureAppServicesSample/AzureAppServicesSample.csproj
@@ -0,0 +1,18 @@
+
+
+
+ netcoreapp3.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/AzureIntegration/sample/AzureAppServicesSample/Properties/launchSettings.json b/src/AzureIntegration/sample/AzureAppServicesSample/Properties/launchSettings.json
new file mode 100644
index 0000000000..93ebc1db03
--- /dev/null
+++ b/src/AzureIntegration/sample/AzureAppServicesSample/Properties/launchSettings.json
@@ -0,0 +1,24 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:64358/",
+ "sslPort": 0
+ }
+ },
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.Server.IISIntegration"
+ }
+ },
+ "AzureAppServicesSample": {
+ "commandName": "IISExpress",
+ "launchBrowser": true
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AzureIntegration/sample/AzureAppServicesSample/Startup.cs b/src/AzureIntegration/sample/AzureAppServicesSample/Startup.cs
new file mode 100644
index 0000000000..4638758d90
--- /dev/null
+++ b/src/AzureIntegration/sample/AzureAppServicesSample/Startup.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Linq;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+
+namespace IISSample
+{
+ public class Startup
+ {
+ public void ConfigureServices(IServiceCollection services)
+ {
+ // These two middleware are registered via an IStartupFilter in UseIISIntegration but you can configure them here.
+ services.Configure(options =>
+ {
+ });
+ services.Configure(options =>
+ {
+ });
+ }
+
+ public void Configure(IApplicationBuilder app, ILoggerFactory loggerfactory)
+ {
+ var logger = loggerfactory.CreateLogger("Requests");
+
+ app.Run(async (context) =>
+ {
+ logger.LogDebug("Received request: " + context.Request.Method + " " + context.Request.Path);
+
+ context.Response.ContentType = "text/plain";
+ await context.Response.WriteAsync("Hello World - " + DateTimeOffset.Now + Environment.NewLine);
+ await context.Response.WriteAsync(Environment.NewLine);
+
+ await context.Response.WriteAsync("Address:" + Environment.NewLine);
+ await context.Response.WriteAsync("Scheme: " + context.Request.Scheme + Environment.NewLine);
+ await context.Response.WriteAsync("Host: " + context.Request.Headers["Host"] + Environment.NewLine);
+ await context.Response.WriteAsync("PathBase: " + context.Request.PathBase.Value + Environment.NewLine);
+ await context.Response.WriteAsync("Path: " + context.Request.Path.Value + Environment.NewLine);
+ await context.Response.WriteAsync("Query: " + context.Request.QueryString.Value + Environment.NewLine);
+ await context.Response.WriteAsync(Environment.NewLine);
+
+ await context.Response.WriteAsync("Connection:" + Environment.NewLine);
+ await context.Response.WriteAsync("RemoteIp: " + context.Connection.RemoteIpAddress + Environment.NewLine);
+ await context.Response.WriteAsync("RemotePort: " + context.Connection.RemotePort + Environment.NewLine);
+ await context.Response.WriteAsync("LocalIp: " + context.Connection.LocalIpAddress + Environment.NewLine);
+ await context.Response.WriteAsync("LocalPort: " + context.Connection.LocalPort + Environment.NewLine);
+ await context.Response.WriteAsync("ClientCert: " + context.Connection.ClientCertificate + Environment.NewLine);
+ await context.Response.WriteAsync(Environment.NewLine);
+
+ await context.Response.WriteAsync("User: " + context.User.Identity.Name + Environment.NewLine);
+ await context.Response.WriteAsync(Environment.NewLine);
+
+ await context.Response.WriteAsync("Headers:" + Environment.NewLine);
+ foreach (var header in context.Request.Headers)
+ {
+ await context.Response.WriteAsync(header.Key + ": " + header.Value + Environment.NewLine);
+ }
+ await context.Response.WriteAsync(Environment.NewLine);
+
+ await context.Response.WriteAsync("Environment Variables:" + Environment.NewLine);
+ var vars = Environment.GetEnvironmentVariables();
+ foreach (var key in vars.Keys.Cast().OrderBy(key => key, StringComparer.OrdinalIgnoreCase))
+ {
+ var value = vars[key];
+ await context.Response.WriteAsync(key + ": " + value + Environment.NewLine);
+ }
+ await context.Response.WriteAsync(Environment.NewLine);
+ });
+ }
+
+ public static void Main(string[] args)
+ {
+ var host = new WebHostBuilder()
+ .ConfigureLogging(factory =>
+ {
+ factory.AddConsole();
+ })
+ .UseKestrel()
+ .UseAzureAppServices()
+ .UseStartup()
+ .Build();
+
+ host.Run();
+ }
+ }
+}
+
diff --git a/src/AzureIntegration/src/Directory.Build.props b/src/AzureIntegration/src/Directory.Build.props
new file mode 100644
index 0000000000..4f07cbc45d
--- /dev/null
+++ b/src/AzureIntegration/src/Directory.Build.props
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServices.HostingStartup/AssemblyInfo.cs b/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServices.HostingStartup/AssemblyInfo.cs
new file mode 100644
index 0000000000..6852ee94a9
--- /dev/null
+++ b/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServices.HostingStartup/AssemblyInfo.cs
@@ -0,0 +1,6 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("Microsoft.AspNetCore.AzureAppServicesIntegration.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
diff --git a/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServices.HostingStartup/AzureAppServicesHostingStartup.cs b/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServices.HostingStartup/AzureAppServicesHostingStartup.cs
new file mode 100644
index 0000000000..5987e7cb58
--- /dev/null
+++ b/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServices.HostingStartup/AzureAppServicesHostingStartup.cs
@@ -0,0 +1,32 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.AspNetCore.Hosting;
+
+[assembly: HostingStartup(typeof(Microsoft.AspNetCore.AzureAppServices.HostingStartup.AzureAppServicesHostingStartup))]
+
+namespace Microsoft.AspNetCore.AzureAppServices.HostingStartup
+{
+ ///
+ /// A dynamic azure lightup experiance
+ ///
+ public class AzureAppServicesHostingStartup : IHostingStartup
+ {
+ private const string HostingStartupName = "AppServices";
+ private const string DiagnosticsFeatureName = "DiagnosticsEnabled";
+
+ ///
+ /// Calls UseAzureAppServices
+ ///
+ ///
+ public void Configure(IWebHostBuilder builder)
+ {
+ var baseConfiguration = HostingStartupConfigurationExtensions.GetBaseConfiguration();
+
+ if (baseConfiguration.IsEnabled(HostingStartupName, DiagnosticsFeatureName))
+ {
+ builder.UseAzureAppServices();
+ }
+ }
+ }
+}
diff --git a/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServices.HostingStartup/HostingStartupConfigurationExtensions.cs b/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServices.HostingStartup/HostingStartupConfigurationExtensions.cs
new file mode 100644
index 0000000000..55ffdc6710
--- /dev/null
+++ b/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServices.HostingStartup/HostingStartupConfigurationExtensions.cs
@@ -0,0 +1,33 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.Extensions.Configuration;
+
+namespace Microsoft.AspNetCore.Hosting
+{
+ internal static class HostingStartupConfigurationExtensions
+ {
+ public static IConfiguration GetBaseConfiguration()
+ {
+ return new ConfigurationBuilder()
+ .AddEnvironmentVariables(prefix: "ASPNETCORE_")
+ .Build();
+ }
+ public static bool IsEnabled(this IConfiguration configuration, string hostingStartupName, string featureName)
+ {
+ if (configuration.TryGetOption(hostingStartupName, featureName, out var value))
+ {
+ value = value.ToLowerInvariant();
+ return value != "false" && value != "0";
+ }
+
+ return true;
+ }
+
+ public static bool TryGetOption(this IConfiguration configuration, string hostingStartupName, string featureName, out string value)
+ {
+ value = configuration[$"HostingStartup:{hostingStartupName}:{featureName}"];
+ return !string.IsNullOrEmpty(value);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServices.HostingStartup/Microsoft.AspNetCore.AzureAppServices.HostingStartup.csproj b/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServices.HostingStartup/Microsoft.AspNetCore.AzureAppServices.HostingStartup.csproj
new file mode 100644
index 0000000000..37da02ef70
--- /dev/null
+++ b/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServices.HostingStartup/Microsoft.AspNetCore.AzureAppServices.HostingStartup.csproj
@@ -0,0 +1,20 @@
+
+
+
+
+
+ ASP.NET Core lightup integration with Azure AppServices.
+ netcoreapp3.0
+ true
+ aspnetcore;azure;appservices
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServices.HostingStartup/baseline.netcore.json b/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServices.HostingStartup/baseline.netcore.json
new file mode 100644
index 0000000000..e294148f6c
--- /dev/null
+++ b/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServices.HostingStartup/baseline.netcore.json
@@ -0,0 +1,39 @@
+{
+ "AssemblyIdentity": "Microsoft.AspNetCore.AzureAppServices.HostingStartup, Version=2.0.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60",
+ "Types": [
+ {
+ "Name": "Microsoft.AspNetCore.AzureAppServices.HostingStartup.AzureAppServicesHostingStartup",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "ImplementedInterfaces": [
+ "Microsoft.AspNetCore.Hosting.IHostingStartup"
+ ],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "Configure",
+ "Parameters": [
+ {
+ "Name": "builder",
+ "Type": "Microsoft.AspNetCore.Hosting.IWebHostBuilder"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Sealed": true,
+ "Virtual": true,
+ "ImplementedInterface": "Microsoft.AspNetCore.Hosting.IHostingStartup",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServices.HostingStartup/baseline.netframework.json b/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServices.HostingStartup/baseline.netframework.json
new file mode 100644
index 0000000000..8aa1ebedab
--- /dev/null
+++ b/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServices.HostingStartup/baseline.netframework.json
@@ -0,0 +1,39 @@
+{
+ "AssemblyIdentity": "Microsoft.AspNetCore.AzureAppServices.HostingStartup, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60",
+ "Types": [
+ {
+ "Name": "Microsoft.AspNetCore.AzureAppServices.HostingStartup.AzureAppServicesHostingStartup",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "ImplementedInterfaces": [
+ "Microsoft.AspNetCore.Hosting.IHostingStartup"
+ ],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "Configure",
+ "Parameters": [
+ {
+ "Name": "builder",
+ "Type": "Microsoft.AspNetCore.Hosting.IWebHostBuilder"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Sealed": true,
+ "Virtual": true,
+ "ImplementedInterface": "Microsoft.AspNetCore.Hosting.IHostingStartup",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServicesIntegration/AppServicesWebHostBuilderExtensions.cs b/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServicesIntegration/AppServicesWebHostBuilderExtensions.cs
new file mode 100644
index 0000000000..09a51bf56d
--- /dev/null
+++ b/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServicesIntegration/AppServicesWebHostBuilderExtensions.cs
@@ -0,0 +1,28 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using Microsoft.Extensions.Logging;
+
+namespace Microsoft.AspNetCore.Hosting
+{
+ public static class AppServicesWebHostBuilderExtensions
+ {
+ ///
+ /// Configures application to use Azure AppServices integration.
+ ///
+ ///
+ ///
+ public static IWebHostBuilder UseAzureAppServices(this IWebHostBuilder hostBuilder)
+ {
+ if (hostBuilder == null)
+ {
+ throw new ArgumentNullException(nameof(hostBuilder));
+ }
+#pragma warning disable 618
+ hostBuilder.ConfigureLogging(builder => builder.AddAzureWebAppDiagnostics());
+#pragma warning restore 618
+ return hostBuilder;
+ }
+ }
+}
diff --git a/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServicesIntegration/Microsoft.AspNetCore.AzureAppServicesIntegration.csproj b/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServicesIntegration/Microsoft.AspNetCore.AzureAppServicesIntegration.csproj
new file mode 100644
index 0000000000..dd72a33a90
--- /dev/null
+++ b/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServicesIntegration/Microsoft.AspNetCore.AzureAppServicesIntegration.csproj
@@ -0,0 +1,17 @@
+
+
+
+ ASP.NET Core integration with Azure AppServices.
+ netcoreapp3.0
+ $(NoWarn);CS1591
+ true
+ true
+ aspnetcore;azure;appservices
+
+
+
+
+
+
+
+
diff --git a/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServicesIntegration/Properties/AssemblyInfo.cs b/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServicesIntegration/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..15e5bb66e2
--- /dev/null
+++ b/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServicesIntegration/Properties/AssemblyInfo.cs
@@ -0,0 +1,6 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Hosting.Azure.AppServices.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
diff --git a/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServicesIntegration/baseline.netcore.json b/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServicesIntegration/baseline.netcore.json
new file mode 100644
index 0000000000..8a54cb63db
--- /dev/null
+++ b/src/AzureIntegration/src/Microsoft.AspNetCore.AzureAppServicesIntegration/baseline.netcore.json
@@ -0,0 +1,32 @@
+{
+ "AssemblyIdentity": "Microsoft.AspNetCore.AzureAppServicesIntegration, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60",
+ "Types": [
+ {
+ "Name": "Microsoft.AspNetCore.Hosting.AppServicesWebHostBuilderExtensions",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "Abstract": true,
+ "Static": true,
+ "Sealed": true,
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "UseAzureAppServices",
+ "Parameters": [
+ {
+ "Name": "hostBuilder",
+ "Type": "Microsoft.AspNetCore.Hosting.IWebHostBuilder"
+ }
+ ],
+ "ReturnType": "Microsoft.AspNetCore.Hosting.IWebHostBuilder",
+ "Static": true,
+ "Extension": true,
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/AzureIntegration/src/Microsoft.Extensions.ApplicationModelDetection/AppModelDetectionResult.cs b/src/AzureIntegration/src/Microsoft.Extensions.ApplicationModelDetection/AppModelDetectionResult.cs
new file mode 100644
index 0000000000..96e53dc52d
--- /dev/null
+++ b/src/AzureIntegration/src/Microsoft.Extensions.ApplicationModelDetection/AppModelDetectionResult.cs
@@ -0,0 +1,12 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace Microsoft.Extensions.ApplicationModelDetection
+{
+ public class AppModelDetectionResult
+ {
+ public RuntimeFramework? Framework { get; set; }
+ public string FrameworkVersion { get; set; }
+ public string AspNetCoreVersion { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/AzureIntegration/src/Microsoft.Extensions.ApplicationModelDetection/AppModelDetector.cs b/src/AzureIntegration/src/Microsoft.Extensions.ApplicationModelDetection/AppModelDetector.cs
new file mode 100644
index 0000000000..e42a8f2918
--- /dev/null
+++ b/src/AzureIntegration/src/Microsoft.Extensions.ApplicationModelDetection/AppModelDetector.cs
@@ -0,0 +1,250 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.IO;
+using System.Linq;
+using System.Reflection.Metadata;
+using System.Reflection.PortableExecutable;
+using System.Xml.Linq;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace Microsoft.Extensions.ApplicationModelDetection
+{
+ public class AppModelDetector
+ {
+ // We use Hosting package to detect AspNetCore version
+ // it contains light-up implemenation that we care about and
+ // would have to be used by aspnet core web apps
+ private const string AspNetCoreAssembly = "Microsoft.AspNetCore.Hosting";
+
+ ///
+ /// Reads the following sources
+ /// - web.config to detect dotnet framework kind
+ /// - *.runtimeconfig.json to detect target framework version
+ /// - *.deps.json to detect Asp.Net Core version
+ /// - Microsoft.AspNetCore.Hosting.dll to detect Asp.Net Core version
+ ///
+ /// The application directory
+ /// The instance containing information about application
+ public AppModelDetectionResult Detect(DirectoryInfo directory)
+ {
+ string entryPoint = null;
+
+ // Try reading web.config and resolving framework and app path
+ var webConfig = directory.GetFiles("web.config").FirstOrDefault();
+
+ bool webConfigExists = webConfig != null;
+ bool? usesDotnetExe = null;
+
+ if (webConfigExists &&
+ TryParseWebConfig(webConfig, out var dotnetExe, out entryPoint))
+ {
+ usesDotnetExe = dotnetExe;
+ }
+
+ // If we found entry point let's look for .deps.json
+ // in some cases it exists in desktop too
+ FileInfo depsJson = null;
+ FileInfo runtimeConfig = null;
+
+ if (!string.IsNullOrWhiteSpace(entryPoint))
+ {
+ depsJson = new FileInfo(Path.ChangeExtension(entryPoint, ".deps.json"));
+ runtimeConfig = new FileInfo(Path.ChangeExtension(entryPoint, ".runtimeconfig.json"));
+ }
+
+ if (depsJson == null || !depsJson.Exists)
+ {
+ depsJson = directory.GetFiles("*.deps.json").FirstOrDefault();
+ }
+
+ if (runtimeConfig == null || !runtimeConfig.Exists)
+ {
+ runtimeConfig = directory.GetFiles("*.runtimeconfig.json").FirstOrDefault();
+ }
+
+ string aspNetCoreVersionFromDeps = null;
+ string aspNetCoreVersionFromDll = null;
+
+
+ // Try to detect ASP.NET Core version from .deps.json
+ if (depsJson != null &&
+ depsJson.Exists &&
+ TryParseDependencies(depsJson, out var aspNetCoreVersion))
+ {
+ aspNetCoreVersionFromDeps = aspNetCoreVersion;
+ }
+
+ // Try to detect ASP.NET Core version from .deps.json
+ var aspNetCoreDll = directory.GetFiles(AspNetCoreAssembly + ".dll").FirstOrDefault();
+ if (aspNetCoreDll != null &&
+ TryParseAssembly(aspNetCoreDll, out aspNetCoreVersion))
+ {
+ aspNetCoreVersionFromDll = aspNetCoreVersion;
+ }
+
+ // Try to detect dotnet core runtime version from runtimeconfig.json
+ string runtimeVersionFromRuntimeConfig = null;
+ if (runtimeConfig != null &&
+ runtimeConfig.Exists)
+ {
+ TryParseRuntimeConfig(runtimeConfig, out runtimeVersionFromRuntimeConfig);
+ }
+
+ var result = new AppModelDetectionResult();
+ if (usesDotnetExe == true)
+ {
+ result.Framework = RuntimeFramework.DotNetCore;
+ result.FrameworkVersion = runtimeVersionFromRuntimeConfig;
+ }
+ else
+ {
+ if (depsJson?.Exists == true &&
+ runtimeConfig?.Exists == true)
+ {
+ result.Framework = RuntimeFramework.DotNetCoreStandalone;
+ }
+ else
+ {
+ result.Framework = RuntimeFramework.DotNetFramework;
+ }
+ }
+
+ result.AspNetCoreVersion = aspNetCoreVersionFromDeps ?? aspNetCoreVersionFromDll;
+
+ return result;
+ }
+
+ private bool TryParseAssembly(FileInfo aspNetCoreDll, out string aspNetCoreVersion)
+ {
+ aspNetCoreVersion = null;
+ try
+ {
+ using (var stream = aspNetCoreDll.OpenRead())
+ using (var peReader = new PEReader(stream))
+ {
+ var metadataReader = peReader.GetMetadataReader();
+ var assemblyDefinition = metadataReader.GetAssemblyDefinition();
+ aspNetCoreVersion = assemblyDefinition.Version.ToString();
+ return true;
+ }
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// Search for Microsoft.AspNetCore.Hosting entry in deps.json and get it's version number
+ ///
+ private bool TryParseDependencies(FileInfo depsJson, out string aspnetCoreVersion)
+ {
+ aspnetCoreVersion = null;
+ try
+ {
+ using (var streamReader = depsJson.OpenText())
+ using (var jsonReader = new JsonTextReader(streamReader))
+ {
+ var json = JObject.Load(jsonReader);
+
+ var libraryPrefix = AspNetCoreAssembly+ "/";
+
+ var library = json.Descendants().OfType().FirstOrDefault(property => property.Name.StartsWith(libraryPrefix));
+ if (library != null)
+ {
+ aspnetCoreVersion = library.Name.Substring(libraryPrefix.Length);
+ return true;
+ }
+ }
+ }
+ catch (Exception)
+ {
+ }
+ return false;
+ }
+
+ private bool TryParseRuntimeConfig(FileInfo runtimeConfig, out string frameworkVersion)
+ {
+ frameworkVersion = null;
+ try
+ {
+ using (var streamReader = runtimeConfig.OpenText())
+ using (var jsonReader = new JsonTextReader(streamReader))
+ {
+ var json = JObject.Load(jsonReader);
+ frameworkVersion = (string)json?["runtimeOptions"]
+ ?["framework"]
+ ?["version"];
+
+ return true;
+ }
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
+
+ private bool TryParseWebConfig(FileInfo webConfig, out bool usesDotnetExe, out string entryPoint)
+ {
+ usesDotnetExe = false;
+ entryPoint = null;
+
+ try
+ {
+ var xdocument = XDocument.Load(webConfig.FullName);
+ var aspNetCoreHandler = xdocument.Root?
+ .Element("system.webServer")
+ .Element("aspNetCore");
+
+ if (aspNetCoreHandler == null)
+ {
+ return false;
+ }
+
+ var processPath = (string) aspNetCoreHandler.Attribute("processPath");
+ var arguments = (string) aspNetCoreHandler.Attribute("arguments");
+
+ if (processPath.EndsWith("dotnet", StringComparison.OrdinalIgnoreCase) ||
+ processPath.EndsWith("dotnet.exe", StringComparison.OrdinalIgnoreCase) &&
+ !string.IsNullOrWhiteSpace(arguments))
+ {
+ usesDotnetExe = true;
+ var entryPointPart = arguments.Split(new[] {" "}, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();
+
+ if (!string.IsNullOrWhiteSpace(entryPointPart))
+ {
+ try
+ {
+ entryPoint = Path.GetFullPath(Path.Combine(webConfig.DirectoryName, entryPointPart));
+ }
+ catch (Exception)
+ {
+ }
+ }
+ }
+ else
+ {
+ usesDotnetExe = false;
+
+ try
+ {
+ entryPoint = Path.GetFullPath(Path.Combine(webConfig.DirectoryName, processPath));
+ }
+ catch (Exception)
+ {
+ }
+ }
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AzureIntegration/src/Microsoft.Extensions.ApplicationModelDetection/Microsoft.Extensions.ApplicationModelDetection.csproj b/src/AzureIntegration/src/Microsoft.Extensions.ApplicationModelDetection/Microsoft.Extensions.ApplicationModelDetection.csproj
new file mode 100644
index 0000000000..ccb195cee2
--- /dev/null
+++ b/src/AzureIntegration/src/Microsoft.Extensions.ApplicationModelDetection/Microsoft.Extensions.ApplicationModelDetection.csproj
@@ -0,0 +1,15 @@
+
+
+ ASP.NET Core integration with Azure AppServices.
+ netcoreapp3.0
+ $(NoWarn);CS1591
+ true
+ true
+ aspnetcore;azure;appservices
+
+
+
+
+
+
+
diff --git a/src/AzureIntegration/src/Microsoft.Extensions.ApplicationModelDetection/RuntimeFramework.cs b/src/AzureIntegration/src/Microsoft.Extensions.ApplicationModelDetection/RuntimeFramework.cs
new file mode 100644
index 0000000000..b182c79eec
--- /dev/null
+++ b/src/AzureIntegration/src/Microsoft.Extensions.ApplicationModelDetection/RuntimeFramework.cs
@@ -0,0 +1,12 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace Microsoft.Extensions.ApplicationModelDetection
+{
+ public enum RuntimeFramework
+ {
+ DotNetCore,
+ DotNetCoreStandalone,
+ DotNetFramework
+ }
+}
\ No newline at end of file
diff --git a/src/AzureIntegration/src/Microsoft.Extensions.ApplicationModelDetection/baseline.netcore.json b/src/AzureIntegration/src/Microsoft.Extensions.ApplicationModelDetection/baseline.netcore.json
new file mode 100644
index 0000000000..d94df11f0a
--- /dev/null
+++ b/src/AzureIntegration/src/Microsoft.Extensions.ApplicationModelDetection/baseline.netcore.json
@@ -0,0 +1,144 @@
+{
+ "AssemblyIdentity": "Microsoft.Extensions.ApplicationModelDetection, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60",
+ "Types": [
+ {
+ "Name": "Microsoft.Extensions.ApplicationModelDetection.AppModelDetectionResult",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "get_Framework",
+ "Parameters": [],
+ "ReturnType": "System.Nullable",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "set_Framework",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.Nullable"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_FrameworkVersion",
+ "Parameters": [],
+ "ReturnType": "System.String",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "set_FrameworkVersion",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.String"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "get_AspNetCoreVersion",
+ "Parameters": [],
+ "ReturnType": "System.String",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "set_AspNetCoreVersion",
+ "Parameters": [
+ {
+ "Name": "value",
+ "Type": "System.String"
+ }
+ ],
+ "ReturnType": "System.Void",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.Extensions.ApplicationModelDetection.AppModelDetector",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "Detect",
+ "Parameters": [
+ {
+ "Name": "directory",
+ "Type": "System.IO.DirectoryInfo"
+ }
+ ],
+ "ReturnType": "Microsoft.Extensions.ApplicationModelDetection.AppModelDetectionResult",
+ "Visibility": "Public",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ },
+ {
+ "Name": "Microsoft.Extensions.ApplicationModelDetection.RuntimeFramework",
+ "Visibility": "Public",
+ "Kind": "Enumeration",
+ "Sealed": true,
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Field",
+ "Name": "DotNetCore",
+ "Parameters": [],
+ "GenericParameter": [],
+ "Literal": "0"
+ },
+ {
+ "Kind": "Field",
+ "Name": "DotNetCoreStandalone",
+ "Parameters": [],
+ "GenericParameter": [],
+ "Literal": "1"
+ },
+ {
+ "Kind": "Field",
+ "Name": "DotNetFramework",
+ "Parameters": [],
+ "GenericParameter": [],
+ "Literal": "2"
+ }
+ ],
+ "GenericParameters": []
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/AzureIntegration/src/Microsoft.Web.Xdt.Extensions/InsertOrAppendAttribute.cs b/src/AzureIntegration/src/Microsoft.Web.Xdt.Extensions/InsertOrAppendAttribute.cs
new file mode 100644
index 0000000000..c2e711cb93
--- /dev/null
+++ b/src/AzureIntegration/src/Microsoft.Web.Xdt.Extensions/InsertOrAppendAttribute.cs
@@ -0,0 +1,144 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Xml;
+using Microsoft.Web.XmlTransform;
+
+namespace Microsoft.Web.Xdt.Extensions
+{
+ ///
+ /// Insert or append to the given attribute
+ ///
+ public class InsertOrAppendAttribute : Transform
+ {
+ ///
+ ///
+ ///
+ public InsertOrAppendAttribute()
+ : base(TransformFlags.UseParentAsTargetNode, MissingTargetMessage.Error)
+ {
+ }
+
+ private string _attributeName;
+
+ ///
+ ///
+ ///
+ protected string AttributeName
+ {
+ get
+ {
+ if (_attributeName == null)
+ {
+ _attributeName = GetArgumentValue("Attribute");
+ }
+ return _attributeName;
+ }
+ }
+
+ ///
+ /// Extracts a value from the arguments provided
+ ///
+ ///
+ ///
+ protected string GetArgumentValue(string name)
+ {
+ if (string.IsNullOrEmpty(name))
+ {
+ throw new ArgumentNullException(nameof(name));
+ }
+
+ string result = null;
+ if (Arguments != null && Arguments.Count > 0)
+ {
+ foreach (var arg in Arguments)
+ {
+ if (!string.IsNullOrEmpty(arg))
+ {
+ var trimmedArg = arg.Trim();
+ if (trimmedArg.StartsWith(name, StringComparison.OrdinalIgnoreCase))
+ {
+ var start = arg.IndexOf('\'');
+ var last = arg.LastIndexOf('\'');
+ if (start <= 0 || last <= 0 || last <= start)
+ {
+ throw new ArgumentException("Expected two ['] characters");
+ }
+
+ var value = trimmedArg.Substring(start, last - start);
+
+ // remove any leading or trailing '
+ value = value.Trim().TrimStart('\'').TrimStart('\'');
+ result = value;
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ ///
+ ///
+ ///
+ protected override void Apply()
+ {
+ if (TargetChildNodes == null || TargetChildNodes.Count == 0)
+ {
+ TargetNode.AppendChild(TransformNode);
+ }
+ else
+ {
+ XmlAttribute transformAtt = null;
+
+ foreach (XmlAttribute att in TransformNode.Attributes)
+ {
+ if (string.Equals(att.Name, AttributeName, StringComparison.OrdinalIgnoreCase))
+ {
+ transformAtt = att;
+ break;
+ }
+ }
+
+ if (transformAtt == null)
+ {
+ throw new InvalidOperationException("No target attribute to append");
+ }
+
+ foreach (XmlNode targetNode in TargetChildNodes)
+ {
+ var foundAttribute = false;
+ foreach (XmlAttribute att in targetNode.Attributes)
+ {
+ if (string.Equals(att.Name, AttributeName, StringComparison.OrdinalIgnoreCase))
+ {
+ foundAttribute = true;
+ if (string.IsNullOrEmpty(att.Value))
+ {
+ att.Value = transformAtt.Value;
+ }
+ else
+ {
+ // TODO: This doesn't compose well with insertOrAppend being applied on the TargetNode.
+ // The target node is created with the children it has in the transform, which means we would
+ // duplicate the value here.
+ if (att.Value == transformAtt.Value)
+ {
+ return;
+ }
+ att.Value = $"{att.Value};{transformAtt.Value}";
+ }
+ }
+ }
+
+ if (!foundAttribute)
+ {
+ var attribute = targetNode.OwnerDocument.CreateAttribute(AttributeName);
+ attribute.Value = transformAtt.Value;
+ targetNode.Attributes.Append(attribute);
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AzureIntegration/src/Microsoft.Web.Xdt.Extensions/Microsoft.Web.Xdt.Extensions.csproj b/src/AzureIntegration/src/Microsoft.Web.Xdt.Extensions/Microsoft.Web.Xdt.Extensions.csproj
new file mode 100644
index 0000000000..4222ec4e2d
--- /dev/null
+++ b/src/AzureIntegration/src/Microsoft.Web.Xdt.Extensions/Microsoft.Web.Xdt.Extensions.csproj
@@ -0,0 +1,14 @@
+
+
+
+ Additional functionality for Xdt transforms.
+ net461
+ true
+ xdt
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/AzureIntegration/src/Microsoft.Web.Xdt.Extensions/baseline.netframework.json b/src/AzureIntegration/src/Microsoft.Web.Xdt.Extensions/baseline.netframework.json
new file mode 100644
index 0000000000..202b401f50
--- /dev/null
+++ b/src/AzureIntegration/src/Microsoft.Web.Xdt.Extensions/baseline.netframework.json
@@ -0,0 +1,53 @@
+{
+ "AssemblyIdentity": "Microsoft.Web.Xdt.Extensions, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60",
+ "Types": [
+ {
+ "Name": "Microsoft.Web.Xdt.Extensions.InsertOrAppendAttribute",
+ "Visibility": "Public",
+ "Kind": "Class",
+ "BaseType": "Microsoft.Web.XmlTransform.Transform",
+ "ImplementedInterfaces": [],
+ "Members": [
+ {
+ "Kind": "Method",
+ "Name": "get_AttributeName",
+ "Parameters": [],
+ "ReturnType": "System.String",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "GetArgumentValue",
+ "Parameters": [
+ {
+ "Name": "name",
+ "Type": "System.String"
+ }
+ ],
+ "ReturnType": "System.String",
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Method",
+ "Name": "Apply",
+ "Parameters": [],
+ "ReturnType": "System.Void",
+ "Virtual": true,
+ "Override": true,
+ "Visibility": "Protected",
+ "GenericParameter": []
+ },
+ {
+ "Kind": "Constructor",
+ "Name": ".ctor",
+ "Parameters": [],
+ "Visibility": "Public",
+ "GenericParameter": []
+ }
+ ],
+ "GenericParameters": []
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/AzureIntegration/test/Directory.Build.props b/src/AzureIntegration/test/Directory.Build.props
new file mode 100644
index 0000000000..f1ae8d1fe4
--- /dev/null
+++ b/src/AzureIntegration/test/Directory.Build.props
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/AzureIntegration/test/Microsoft.AspNetCore.AzureAppServicesIntegration.Tests/AppServicesWebHostBuilderExtensionsTest.cs b/src/AzureIntegration/test/Microsoft.AspNetCore.AzureAppServicesIntegration.Tests/AppServicesWebHostBuilderExtensionsTest.cs
new file mode 100644
index 0000000000..d3aa47bba5
--- /dev/null
+++ b/src/AzureIntegration/test/Microsoft.AspNetCore.AzureAppServicesIntegration.Tests/AppServicesWebHostBuilderExtensionsTest.cs
@@ -0,0 +1,24 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Moq;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Hosting.Azure.AppServices.Tests
+{
+ public class AppServicesWebHostBuilderExtensionsTest
+ {
+ [Fact]
+ public void UseAzureAppServices_RegisterLogger()
+ {
+ var mock = new Mock();
+
+ mock.Object.UseAzureAppServices();
+
+ mock.Verify(builder => builder.ConfigureServices(It.IsNotNull>()), Times.Once);
+ }
+ }
+}
diff --git a/src/AzureIntegration/test/Microsoft.AspNetCore.AzureAppServicesIntegration.Tests/Microsoft.AspNetCore.AzureAppServicesIntegration.Tests.csproj b/src/AzureIntegration/test/Microsoft.AspNetCore.AzureAppServicesIntegration.Tests/Microsoft.AspNetCore.AzureAppServicesIntegration.Tests.csproj
new file mode 100644
index 0000000000..039fe663b8
--- /dev/null
+++ b/src/AzureIntegration/test/Microsoft.AspNetCore.AzureAppServicesIntegration.Tests/Microsoft.AspNetCore.AzureAppServicesIntegration.Tests.csproj
@@ -0,0 +1,16 @@
+
+
+
+ netcoreapp3.0
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/AzureIntegration/test/Microsoft.Extensions.ApplicationModelDetection.Tests/AppModelTests.cs b/src/AzureIntegration/test/Microsoft.Extensions.ApplicationModelDetection.Tests/AppModelTests.cs
new file mode 100644
index 0000000000..02e420d2e4
--- /dev/null
+++ b/src/AzureIntegration/test/Microsoft.Extensions.ApplicationModelDetection.Tests/AppModelTests.cs
@@ -0,0 +1,229 @@
+// // Copyright (c) .NET Foundation. All rights reserved.
+// // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.IO;
+using Microsoft.AspNetCore.Hosting;
+using Xunit;
+
+namespace Microsoft.Extensions.ApplicationModelDetection.Tests
+{
+ public class AppModelTests
+ {
+ private const string EmptyWebConfig = @"";
+
+ [Theory]
+ [InlineData("dotnet")]
+ [InlineData("dotnet.exe")]
+ [InlineData("%HOME%/dotnet")]
+ [InlineData("%HOME%/dotnet.exe")]
+ [InlineData("DoTNeT.ExE")]
+ public void DetectsCoreFrameworkFromWebConfig(string processPath)
+ {
+ using (var temp = new TemporaryDirectory()
+ .WithFile("web.config",GenerateWebConfig(processPath, ".\\app.dll")))
+ {
+ var result = new AppModelDetector().Detect(temp.Directory);
+ Assert.Equal(RuntimeFramework.DotNetCore, result.Framework);
+ }
+ }
+
+ [Theory]
+ [InlineData("app")]
+ [InlineData("app.exe")]
+ [InlineData("%HOME%/app")]
+ [InlineData("%HOME%/app.exe")]
+ public void DetectsFullFrameworkFromWebConfig(string processPath)
+ {
+ using (var temp = new TemporaryDirectory()
+ .WithFile("web.config", GenerateWebConfig(processPath, ".\\app.dll")))
+ {
+ var result = new AppModelDetector().Detect(temp.Directory);
+ Assert.Equal(RuntimeFramework.DotNetFramework, result.Framework);
+ }
+ }
+
+ [Theory]
+ [InlineData("2.0.0")]
+ [InlineData("2.0.0-preview1")]
+ [InlineData("1.1.3")]
+ public void DetectsRuntimeVersionFromRuntimeConfig(string runtimeVersion)
+ {
+ using (var temp = new TemporaryDirectory()
+ .WithFile("web.config", GenerateWebConfig("dotnet", ".\\app.dll"))
+ .WithFile("app.runtimeconfig.json", @"{
+ ""runtimeOptions"": {
+ ""tfm"": ""netcoreapp2.0"",
+ ""framework"": {
+ ""name"": ""Microsoft.NETCore.App"",
+ ""version"": """+ runtimeVersion + @"""
+ },
+ ""configProperties"": {
+ ""System.GC.Server"": true
+ }
+ }
+}"))
+ {
+ var result = new AppModelDetector().Detect(temp.Directory);
+ Assert.Equal(RuntimeFramework.DotNetCore, result.Framework);
+ Assert.Equal(runtimeVersion, result.FrameworkVersion);
+ }
+ }
+
+
+ [Theory]
+ [InlineData("2.0.0")]
+ [InlineData("2.0.0-preview1")]
+ [InlineData("1.1.3")]
+ public void DetectsRuntimeVersionFromRuntimeConfigWitoutEntryPoint(string runtimeVersion)
+ {
+ using (var temp = new TemporaryDirectory()
+ .WithFile("web.config", GenerateWebConfig("dotnet", "%HOME%\\app.dll"))
+ .WithFile("app.runtimeconfig.json", @"{
+ ""runtimeOptions"": {
+ ""tfm"": ""netcoreapp2.0"",
+ ""framework"": {
+ ""name"": ""Microsoft.NETCore.App"",
+ ""version"": """+ runtimeVersion + @"""
+ },
+ ""configProperties"": {
+ ""System.GC.Server"": true
+ }
+ }
+}"))
+ {
+ var result = new AppModelDetector().Detect(temp.Directory);
+ Assert.Equal(RuntimeFramework.DotNetCore, result.Framework);
+ Assert.Equal(runtimeVersion, result.FrameworkVersion);
+ }
+ }
+
+ [Theory]
+ [InlineData("2.0.0")]
+ [InlineData("2.0.0-preview1")]
+ [InlineData("1.1.3")]
+ public void DetectsAspNetCoreVersionFromDepsFile(string runtimeVersion)
+ {
+ using (var temp = new TemporaryDirectory()
+ .WithFile("web.config", GenerateWebConfig("dotnet", "app.dll"))
+ .WithFile("app.deps.json", @"{
+ ""targets"": {
+ "".NETCoreApp,Version=v2.7"": {
+ ""Microsoft.AspNetCore.Hosting/" + runtimeVersion + @""": { }
+ }
+ }
+}"))
+ {
+ var result = new AppModelDetector().Detect(temp.Directory);
+ Assert.Equal(RuntimeFramework.DotNetCore, result.Framework);
+ Assert.Equal(runtimeVersion, result.AspNetCoreVersion);
+ }
+ }
+
+ [Theory]
+ [InlineData("2.0.0")]
+ [InlineData("2.0.0-preview1")]
+ [InlineData("1.1.3")]
+ public void DetectsAspNetCoreVersionFromDepsFileWithoutEntryPoint(string runtimeVersion)
+ {
+ using (var temp = new TemporaryDirectory()
+ .WithFile("web.config", GenerateWebConfig("dotnet", "%HOME%\\app.dll"))
+ .WithFile("app.deps.json", @"{
+ ""targets"": {
+ "".NETCoreApp,Version=v2.7"": {
+ ""Microsoft.AspNetCore.Hosting/" + runtimeVersion + @""": { }
+ }
+ }
+}"))
+ {
+ var result = new AppModelDetector().Detect(temp.Directory);
+ Assert.Equal(RuntimeFramework.DotNetCore, result.Framework);
+ Assert.Equal(runtimeVersion, result.AspNetCoreVersion);
+ }
+ }
+
+ [Fact]
+ public void DetectsFullFrameworkWhenWebConfigExists()
+ {
+ using (var temp = new TemporaryDirectory()
+ .WithFile("web.config", EmptyWebConfig))
+ {
+ var result = new AppModelDetector().Detect(temp.Directory);
+ Assert.Equal(RuntimeFramework.DotNetFramework, result.Framework);
+ }
+ }
+
+ [Fact]
+ public void DetectsStandalone_WhenBothDepsAndRuntimeConfigExist()
+ {
+ using (var temp = new TemporaryDirectory()
+ .WithFile("web.config", GenerateWebConfig("app.exe", ""))
+ .WithFile("app.runtimeconfig.json", "{}")
+ .WithFile("app.deps.json", "{}"))
+ {
+ var result = new AppModelDetector().Detect(temp.Directory);
+ Assert.Equal(RuntimeFramework.DotNetCoreStandalone, result.Framework);
+ }
+ }
+
+ [Fact]
+ public void DetectsAspNetCoreVersionFromHostingDll()
+ {
+ using (var temp = new TemporaryDirectory()
+ .WithFile(typeof(WebHostBuilder).Assembly.Location))
+ {
+ var result = new AppModelDetector().Detect(temp.Directory);
+ Assert.Equal(typeof(WebHostBuilder).Assembly.GetName().Version.ToString(), result.AspNetCoreVersion);
+ }
+ }
+
+ private static string GenerateWebConfig(string processPath, string arguments)
+ {
+ return $@"
+
+
+
+
+
+
+
+
+";
+ }
+
+ private class TemporaryDirectory: IDisposable
+ {
+ public TemporaryDirectory()
+ {
+ Directory = new DirectoryInfo(Path.GetTempPath())
+ .CreateSubdirectory(Guid.NewGuid().ToString("N"));
+ }
+
+ public DirectoryInfo Directory { get; }
+
+ public void Dispose()
+ {
+ try
+ {
+ Directory.Delete(true);
+ }
+ catch (IOException)
+ {
+ }
+ }
+
+ public TemporaryDirectory WithFile(string name, string value)
+ {
+ File.WriteAllText(Path.Combine(Directory.FullName, name), value);
+ return this;
+ }
+
+
+ public TemporaryDirectory WithFile(string name)
+ {
+ File.Copy(name, Path.Combine(Directory.FullName, Path.GetFileName(name)));
+ return this;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AzureIntegration/test/Microsoft.Extensions.ApplicationModelDetection.Tests/Microsoft.Extensions.ApplicationModelDetection.Tests.csproj b/src/AzureIntegration/test/Microsoft.Extensions.ApplicationModelDetection.Tests/Microsoft.Extensions.ApplicationModelDetection.Tests.csproj
new file mode 100644
index 0000000000..b95c066f61
--- /dev/null
+++ b/src/AzureIntegration/test/Microsoft.Extensions.ApplicationModelDetection.Tests/Microsoft.Extensions.ApplicationModelDetection.Tests.csproj
@@ -0,0 +1,11 @@
+
+
+
+ netcoreapp3.0
+
+
+
+
+
+
+
diff --git a/src/AzureIntegration/test/Microsoft.Web.Xdt.Extensions.Tests/InsertOrAppendAttributeTests.cs b/src/AzureIntegration/test/Microsoft.Web.Xdt.Extensions.Tests/InsertOrAppendAttributeTests.cs
new file mode 100644
index 0000000000..546631f14f
--- /dev/null
+++ b/src/AzureIntegration/test/Microsoft.Web.Xdt.Extensions.Tests/InsertOrAppendAttributeTests.cs
@@ -0,0 +1,109 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.IO;
+using System.Xml;
+using Microsoft.Web.XmlTransform;
+using Xunit;
+
+namespace Microsoft.Web.Xdt.Extensions
+{
+ public class InsertOrAppendAttributeTests
+ {
+ [Fact]
+ public void InsertOrAppend_NoExesitingLine_InsertsLine()
+ {
+ var transform = new XmlTransformation(Path.GetFullPath("transform.xdt"));
+ var doc = new XmlDocument();
+ doc.Load("config_empty.xml");
+ Assert.True(transform.Apply(doc));
+
+ Assert.Equal(2, doc.ChildNodes.Count);
+ var configurationNode = doc["configuration"];
+
+ Assert.Equal(2, configurationNode.ChildNodes.Count);
+
+ var firstChild = configurationNode.FirstChild;
+ Assert.Equal("add", firstChild.Name);
+ Assert.Equal("KeyName1", firstChild.Attributes["name"].Value);
+ Assert.Equal("InsertValue1", firstChild.Attributes["value"].Value);
+
+ var secondChild = firstChild.NextSibling;
+ Assert.Equal("add", secondChild.Name);
+ Assert.Equal("KeyName2", secondChild.Attributes["name"].Value);
+ Assert.Equal("InsertValue2", secondChild.Attributes["value"].Value);
+ }
+
+ [Fact]
+ public void InsertOrAppend_LineExistsButNoValueField_FieldInserted()
+ {
+ var transform = new XmlTransformation(Path.GetFullPath("transform.xdt"));
+ var doc = new XmlDocument();
+ doc.Load("config_existingline.xml");
+ Assert.True(transform.Apply(doc));
+
+ Assert.Equal(2, doc.ChildNodes.Count);
+ var configurationNode = doc["configuration"];
+
+ Assert.Equal(2, configurationNode.ChildNodes.Count);
+
+ var firstChild = configurationNode.FirstChild;
+ Assert.Equal("add", firstChild.Name);
+ Assert.Equal("KeyName1", firstChild.Attributes["name"].Value);
+ Assert.Equal("InsertValue1", firstChild.Attributes["value"].Value);
+
+ var secondChild = firstChild.NextSibling;
+ Assert.Equal("add", secondChild.Name);
+ Assert.Equal("KeyName2", secondChild.Attributes["name"].Value);
+ Assert.Equal("InsertValue2", secondChild.Attributes["value"].Value);
+ }
+
+ [Fact]
+ public void InsertOrAppend_ExistingEmptyValue_InsertsValue()
+ {
+ var transform = new XmlTransformation(Path.GetFullPath("transform.xdt"));
+ var doc = new XmlDocument();
+ doc.Load("config_existingemptyvalue.xml");
+ Assert.True(transform.Apply(doc));
+
+ Assert.Equal(2, doc.ChildNodes.Count);
+ var configurationNode = doc["configuration"];
+
+ Assert.Equal(2, configurationNode.ChildNodes.Count);
+
+ var firstChild = configurationNode.FirstChild;
+ Assert.Equal("add", firstChild.Name);
+ Assert.Equal("KeyName1", firstChild.Attributes["name"].Value);
+ Assert.Equal("InsertValue1", firstChild.Attributes["value"].Value);
+
+ var secondChild = firstChild.NextSibling;
+ Assert.Equal("add", secondChild.Name);
+ Assert.Equal("KeyName2", secondChild.Attributes["name"].Value);
+ Assert.Equal("InsertValue2", secondChild.Attributes["value"].Value);
+ }
+
+ [Fact]
+ public void InsertOrAppend_ExistingValue_AppendsValue()
+ {
+ var transform = new XmlTransformation(Path.GetFullPath("transform.xdt"));
+ var doc = new XmlDocument();
+ doc.Load("config_existingvalue.xml");
+ Assert.True(transform.Apply(doc));
+
+ Assert.Equal(2, doc.ChildNodes.Count);
+ var configurationNode = doc["configuration"];
+
+ Assert.Equal(2, configurationNode.ChildNodes.Count);
+
+ var firstChild = configurationNode.FirstChild;
+ Assert.Equal("add", firstChild.Name);
+ Assert.Equal("KeyName1", firstChild.Attributes["name"].Value);
+ Assert.Equal("ExistingValue1;InsertValue1", firstChild.Attributes["value"].Value);
+
+ var secondChild = firstChild.NextSibling;
+ Assert.Equal("add", secondChild.Name);
+ Assert.Equal("KeyName2", secondChild.Attributes["name"].Value);
+ Assert.Equal("ExistingValue2;InsertValue2", secondChild.Attributes["value"].Value);
+ }
+ }
+}
diff --git a/src/AzureIntegration/test/Microsoft.Web.Xdt.Extensions.Tests/Microsoft.Web.Xdt.Extensions.Tests.csproj b/src/AzureIntegration/test/Microsoft.Web.Xdt.Extensions.Tests/Microsoft.Web.Xdt.Extensions.Tests.csproj
new file mode 100644
index 0000000000..026516e0ae
--- /dev/null
+++ b/src/AzureIntegration/test/Microsoft.Web.Xdt.Extensions.Tests/Microsoft.Web.Xdt.Extensions.Tests.csproj
@@ -0,0 +1,33 @@
+
+
+
+ net461
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
diff --git a/src/AzureIntegration/test/Microsoft.Web.Xdt.Extensions.Tests/config_empty.xml b/src/AzureIntegration/test/Microsoft.Web.Xdt.Extensions.Tests/config_empty.xml
new file mode 100644
index 0000000000..1156926c52
--- /dev/null
+++ b/src/AzureIntegration/test/Microsoft.Web.Xdt.Extensions.Tests/config_empty.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/src/AzureIntegration/test/Microsoft.Web.Xdt.Extensions.Tests/config_existingemptyvalue.xml b/src/AzureIntegration/test/Microsoft.Web.Xdt.Extensions.Tests/config_existingemptyvalue.xml
new file mode 100644
index 0000000000..66299d8c9e
--- /dev/null
+++ b/src/AzureIntegration/test/Microsoft.Web.Xdt.Extensions.Tests/config_existingemptyvalue.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/AzureIntegration/test/Microsoft.Web.Xdt.Extensions.Tests/config_existingline.xml b/src/AzureIntegration/test/Microsoft.Web.Xdt.Extensions.Tests/config_existingline.xml
new file mode 100644
index 0000000000..6a474e9808
--- /dev/null
+++ b/src/AzureIntegration/test/Microsoft.Web.Xdt.Extensions.Tests/config_existingline.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/AzureIntegration/test/Microsoft.Web.Xdt.Extensions.Tests/config_existingvalue.xml b/src/AzureIntegration/test/Microsoft.Web.Xdt.Extensions.Tests/config_existingvalue.xml
new file mode 100644
index 0000000000..f8a5cf3533
--- /dev/null
+++ b/src/AzureIntegration/test/Microsoft.Web.Xdt.Extensions.Tests/config_existingvalue.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/AzureIntegration/test/Microsoft.Web.Xdt.Extensions.Tests/transform.xdt b/src/AzureIntegration/test/Microsoft.Web.Xdt.Extensions.Tests/transform.xdt
new file mode 100644
index 0000000000..f62924ab31
--- /dev/null
+++ b/src/AzureIntegration/test/Microsoft.Web.Xdt.Extensions.Tests/transform.xdt
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/AzureIntegration/version.props b/src/AzureIntegration/version.props
new file mode 100644
index 0000000000..bcf920cf80
--- /dev/null
+++ b/src/AzureIntegration/version.props
@@ -0,0 +1,12 @@
+
+
+ 3.0.0
+ alpha1
+ $(VersionPrefix)
+ $(VersionPrefix)-$(VersionSuffix)-final
+ t000
+ a-
+ $(FeatureBranchVersionPrefix)$(VersionSuffix)-$([System.Text.RegularExpressions.Regex]::Replace('$(FeatureBranchVersionSuffix)', '[^\w-]', '-'))
+ $(VersionSuffix)-$(BuildNumber)
+
+