From 1bdab1cf1f59b2599e5b000b81cd151e55c7d9e3 Mon Sep 17 00:00:00 2001 From: Kirsten Kluge Date: Sun, 9 Jan 2022 23:17:46 +0100 Subject: [PATCH 1/5] Initial 2.0 commit - Update to dotnet 6 - Update to the new minimal API model - Update the Azure packages - Update Build pipeline - Update GitVersion - Remove Gitversion script, insteed make use of the dotnet tool version - Add route for /config and /config.json - Update Editor Config - Add Azure template for easy sample deployment +semver: major --- .config/dotnet-tools.json | 12 ++ .editorconfig | 159 +++++++++++------- .vscode/launch.json | 19 +-- .vscode/tasks.json | 4 +- GitVersion.yml | 1 + README.md | 2 + azure/azuredeploy.json | 145 ++++++++++++++++ global.json | 2 +- pipelines/EasyConfig.yaml | 21 ++- pipelines/tools/Run-GitVersion.ps1 | 25 --- publish.ps1 | 6 +- .../KeyVaultAppConfiguration.cs | 55 ++++++ .../Controllers/ConfigController.cs | 100 ++++++----- .../EasyConfig.SiteExtension.csproj | 16 +- .../Logging/Logging.cs | 22 +++ .../PrefixKeyVaultSecretManager.cs | 47 +++--- src/EasyConfig.SiteExtension/Program.cs | 83 ++------- .../Properties/launchSettings.json | 21 +-- src/EasyConfig.SiteExtension/Startup.cs | 39 ----- .../appsettings.Development.json | 14 ++ src/EasyConfig.SiteExtension/appsettings.json | 3 +- 21 files changed, 472 insertions(+), 324 deletions(-) create mode 100644 .config/dotnet-tools.json create mode 100644 azure/azuredeploy.json delete mode 100644 pipelines/tools/Run-GitVersion.ps1 create mode 100644 src/EasyConfig.SiteExtension/AppConfiguration/KeyVaultAppConfiguration.cs create mode 100644 src/EasyConfig.SiteExtension/Logging/Logging.cs delete mode 100644 src/EasyConfig.SiteExtension/Startup.cs create mode 100644 src/EasyConfig.SiteExtension/appsettings.Development.json diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json new file mode 100644 index 0000000..781847a --- /dev/null +++ b/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "gitversion.tool": { + "version": "5.8.1", + "commands": [ + "dotnet-gitversion" + ] + } + } +} \ No newline at end of file diff --git a/.editorconfig b/.editorconfig index ea05783..a5582fa 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,5 +1,5 @@ -# Version: 1.3.2 (Using https://semver.org/) -# Updated: 2019-08-04 +# Version: 4.0.0 (Using https://semver.org/) +# Updated: 2021-10-12 # See https://github.com/RehanSaeed/EditorConfig/releases for release notes. # See https://github.com/RehanSaeed/EditorConfig for updates to this file. # See http://EditorConfig.org for more information about .editorconfig files. @@ -28,15 +28,15 @@ trim_trailing_whitespace = true indent_style = tab # Visual Studio XML Project Files -[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] +[*.{csproj,vbproj,vcxproj.filters,proj,projitems,shproj}] indent_size = 2 -# Various XML Configuration Files +# XML Configuration Files [*.{xml,config,props,targets,nuspec,resx,ruleset,vsixmanifest,vsct}] indent_size = 2 # JSON Files -[*.{json,json5}] +[*.{json,json5,webmanifest}] indent_size = 2 # YAML Files @@ -48,7 +48,7 @@ indent_size = 2 trim_trailing_whitespace = false # Web Files -[*.{htm,html,js,ts,tsx,css,sass,scss,less,svg,vue}] +[*.{htm,html,js,jsm,ts,tsx,cjs,cts,ctsx,mjs,mts,mtsx,css,sass,scss,less,pcss,svg,vue}] indent_size = 2 # Batch Files @@ -59,69 +59,81 @@ end_of_line = crlf [*.sh] end_of_line = lf +# Makefiles +[Makefile] +indent_style = tab + ########################################## -# .NET Language Conventions -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions +# Default .NET Code Style Severities +# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/configuration-options#scope ########################################## -# .NET Code Style Settings -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#net-code-style-settings -[*.{cs,csx,cake,vb}] +[*.{cs,csx,cake,vb,vbx}] +# Default Severity for all .NET Code Style rules below +dotnet_analyzer_diagnostic.severity = warning + +########################################## +# Language Rules +# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/language-rules +########################################## + +# .NET Style Rules +# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/language-rules#net-style-rules +[*.{cs,csx,cake,vb,vbx}] # "this." and "Me." qualifiers -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#this-and-me dotnet_style_qualification_for_field = true:warning dotnet_style_qualification_for_property = true:warning dotnet_style_qualification_for_method = true:warning dotnet_style_qualification_for_event = true:warning # Language keywords instead of framework type names for type references -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#language-keywords dotnet_style_predefined_type_for_locals_parameters_members = true:warning dotnet_style_predefined_type_for_member_access = true:warning # Modifier preferences -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#normalize-modifiers dotnet_style_require_accessibility_modifiers = always:warning -csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async -visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:warning +visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:warning dotnet_style_readonly_field = true:warning # Parentheses preferences -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#parentheses-preferences dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:warning dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:warning dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:warning -dotnet_style_parentheses_in_other_operators = never_if_unnecessary:suggestion +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:warning # Expression-level preferences -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#expression-level-preferences dotnet_style_object_initializer = true:warning dotnet_style_collection_initializer = true:warning dotnet_style_explicit_tuple_names = true:warning dotnet_style_prefer_inferred_tuple_names = true:warning dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning dotnet_style_prefer_auto_properties = true:warning -dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning dotnet_style_prefer_conditional_expression_over_assignment = false:suggestion +dotnet_diagnostic.IDE0045.severity = suggestion dotnet_style_prefer_conditional_expression_over_return = false:suggestion +dotnet_diagnostic.IDE0046.severity = suggestion dotnet_style_prefer_compound_assignment = true:warning +dotnet_style_prefer_simplified_interpolation = true:warning +dotnet_style_prefer_simplified_boolean_expressions = true:warning # Null-checking preferences -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#null-checking-preferences dotnet_style_coalesce_expression = true:warning dotnet_style_null_propagation = true:warning -# Parameter preferences -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#parameter-preferences -dotnet_code_quality_unused_parameters = all:warning -# More style options (Undocumented) -# https://github.com/MicrosoftDocs/visualstudio-docs/issues/3641 -dotnet_style_operator_placement_when_wrapping = end_of_line - -# C# Code Style Settings -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#c-code-style-settings +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning +# File header preferences +# file_header_template = \n© PROJECT-AUTHOR\n +# If you use StyleCop, you'll need to disable SA1636: File header copyright text should match. +# dotnet_diagnostic.SA1636.severity = none +# Undocumented +dotnet_style_operator_placement_when_wrapping = end_of_line:warning +csharp_style_prefer_null_check_over_type_check = true:warning +dotnet_style_namespace_match_folder = true:suggestion +dotnet_diagnostic.IDE0130.severity = suggestion + +# C# Style Rules +# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/language-rules#c-style-rules [*.{cs,csx,cake}] -# Implicit and explicit types -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#implicit-and-explicit-types +# 'var' preferences csharp_style_var_for_built_in_types = true:warning csharp_style_var_when_type_is_apparent = true:warning csharp_style_var_elsewhere = true:warning # Expression-bodied members -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#expression-bodied-members csharp_style_expression_bodied_methods = true:warning csharp_style_expression_bodied_constructors = true:warning csharp_style_expression_bodied_operators = true:warning @@ -130,47 +142,66 @@ csharp_style_expression_bodied_indexers = true:warning csharp_style_expression_bodied_accessors = true:warning csharp_style_expression_bodied_lambdas = true:warning csharp_style_expression_bodied_local_functions = true:warning -# Pattern matching -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#pattern-matching +# Pattern matching preferences csharp_style_pattern_matching_over_is_with_cast_check = true:warning csharp_style_pattern_matching_over_as_with_null_check = true:warning -# Inlined variable declarations -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#inlined-variable-declarations -csharp_style_inlined_variable_declaration = true:warning +csharp_style_prefer_switch_expression = true:warning +csharp_style_prefer_pattern_matching = true:warning +csharp_style_prefer_not_pattern = true:warning # Expression-level preferences -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#expression-level-preferences +csharp_style_inlined_variable_declaration = true:warning csharp_prefer_simple_default_expression = true:warning +csharp_style_pattern_local_over_anonymous_function = true:warning +csharp_style_deconstructed_variable_declaration = true:warning +csharp_style_prefer_index_operator = true:warning +csharp_style_prefer_range_operator = true:warning +csharp_style_implicit_object_creation_when_type_is_apparent = true:warning # "Null" checking preferences -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#c-null-checking-preferences csharp_style_throw_expression = true:warning csharp_style_conditional_delegate_call = true:warning # Code block preferences -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#code-block-preferences csharp_prefer_braces = true:warning -# Unused value preferences -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#unused-value-preferences -csharp_style_unused_value_expression_statement_preference = discard_variable:suggestion -csharp_style_unused_value_assignment_preference = discard_variable:suggestion -# Index and range preferences -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#index-and-range-preferences -csharp_style_prefer_index_operator = true:warning -csharp_style_prefer_range_operator = true:warning -# Miscellaneous preferences -# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#miscellaneous-preferences -csharp_style_deconstructed_variable_declaration = true:warning -csharp_style_pattern_local_over_anonymous_function = true:warning +csharp_prefer_simple_using_statement = true:suggestion +dotnet_diagnostic.IDE0063.severity = suggestion +# 'using' directive preferences csharp_using_directive_placement = inside_namespace:warning +# Modifier preferences csharp_prefer_static_local_function = true:warning -csharp_prefer_simple_using_statement = false:warning +# Undocumented +csharp_style_namespace_declarations = file_scoped:warning ########################################## -# .NET Formatting Conventions -# https://docs.microsoft.com/visualstudio/ide/editorconfig-code-style-settings-reference#formatting-conventions +# Unnecessary Code Rules +# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/unnecessary-code-rules ########################################## -# Organize usings -# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#organize-using-directives +# .NET Unnecessary code rules +[*.{cs,csx,cake,vb,vbx}] +dotnet_code_quality_unused_parameters = all:warning +dotnet_remove_unnecessary_suppression_exclusions = none:warning + +# C# Unnecessary code rules +[*.{cs,csx,cake}] +csharp_style_unused_value_expression_statement_preference = discard_variable:suggestion +dotnet_diagnostic.IDE0058.severity = suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +dotnet_diagnostic.IDE0059.severity = suggestion + +########################################## +# Formatting Rules +# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/formatting-rules +########################################## + +# .NET formatting rules +# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/formatting-rules#net-formatting-rules +[*.{cs,csx,cake,vb,vbx}] +# Organize using directives dotnet_sort_system_directives_first = true +dotnet_separate_import_directive_groups = false + +# C# formatting rules +# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/formatting-rules#c-formatting-rules +[*.{cs,csx,cake}] # Newline options # https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#new-line-options csharp_new_line_before_open_brace = all @@ -212,17 +243,17 @@ csharp_space_around_declaration_statements = false csharp_space_before_open_square_brackets = false csharp_space_between_empty_square_brackets = false csharp_space_between_square_brackets = false -# Wrapping options +# Wrap options # https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#wrap-options csharp_preserve_single_line_statements = false csharp_preserve_single_line_blocks = true ########################################## -# .NET Naming Conventions -# https://docs.microsoft.com/visualstudio/ide/editorconfig-naming-conventions +# .NET Naming Rules +# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/naming-rules ########################################## -[*.{cs,csx,cake,vb}] +[*.{cs,csx,cake,vb,vbx}] ########################################## # Styles @@ -244,7 +275,7 @@ dotnet_naming_style.prefix_type_parameters_with_t_style.required_prefix = T dotnet_naming_style.disallowed_style.capitalization = pascal_case dotnet_naming_style.disallowed_style.required_prefix = ____RULE_VIOLATION____ dotnet_naming_style.disallowed_style.required_suffix = ____RULE_VIOLATION____ -# internal_error_style - This style should never occur... if it does, it's indicates a bug in file or in the parser using the file +# internal_error_style - This style should never occur... if it does, it indicates a bug in file or in the parser using the file dotnet_naming_style.internal_error_style.capitalization = pascal_case dotnet_naming_style.internal_error_style.required_prefix = ____INTERNAL_ERROR____ dotnet_naming_style.internal_error_style.required_suffix = ____INTERNAL_ERROR____ @@ -414,4 +445,4 @@ dotnet_naming_rule.parameters_rule.severity = warning # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. -########################################## \ No newline at end of file +########################################## diff --git a/.vscode/launch.json b/.vscode/launch.json index b17ed16..d477459 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,23 +1,21 @@ { - // Use IntelliSense to find out which attributes exist for C# debugging - // Use hover for the description of the existing attributes - // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md - "version": "0.2.0", - "configurations": [ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ { "name": ".NET Core Launch (web)", "type": "coreclr", "request": "launch", "preLaunchTask": "build", - // If you have changed target frameworks, make sure to update the program path. - "program": "${workspaceFolder}/src/EasyConfig.SiteExtension/bin/Debug/netcoreapp3.0/EasyConfig.SiteExtension.dll", + "program": "${workspaceFolder}/src/EasyConfig.SiteExtension/bin/Debug/net6.0/EasyConfig.SiteExtension.dll", "args": [], "cwd": "${workspaceFolder}/src/EasyConfig.SiteExtension", "stopAtEntry": false, - // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser "serverReadyAction": { "action": "openExternally", - "pattern": "^\\s*Now listening on:\\s+(https?://\\S+)" + "pattern": "\\bNow listening on:\\s+(https?://\\S+)" }, "env": { "ASPNETCORE_ENVIRONMENT": "Development", @@ -36,8 +34,7 @@ { "name": ".NET Core Attach", "type": "coreclr", - "request": "attach", - "processId": "${command:pickProcess}" + "request": "attach" } ] } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 097cc56..791ec40 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -3,12 +3,12 @@ "tasks": [ { "label": "build", - "command": "dotnet", - "type": "process", "group": { "kind": "build", "isDefault": true }, + "command": "dotnet", + "type": "process", "args": [ "build", "${workspaceFolder}/src/EasyConfig.SiteExtension/EasyConfig.SiteExtension.csproj", diff --git a/GitVersion.yml b/GitVersion.yml index 4726cb4..4db86d1 100644 --- a/GitVersion.yml +++ b/GitVersion.yml @@ -6,5 +6,6 @@ minor-version-bump-message: '\+semver:\s?(feature|minor)' patch-version-bump-message: '\+semver:\s?(fix|patch)' no-bump-message: '\+semver:\s?(none|skip)' commit-message-incrementing: Enabled +update-build-number: false ignore: sha: [] \ No newline at end of file diff --git a/README.md b/README.md index f3e29be..1a73485 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ > provided by [codez.one](https://codez.one) +[![Deploy To Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fcodez-one%2FEasyConfig%2Fmaster%2Fazure%2Fazuredeploy.json) + ## Introduction An endpoint for SPA client apps hosted in an Azure Web App to get configuration values stored in environment variables or Azure KeyVault. diff --git a/azure/azuredeploy.json b/azure/azuredeploy.json new file mode 100644 index 0000000..85f20cd --- /dev/null +++ b/azure/azuredeploy.json @@ -0,0 +1,145 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + }, + "variables": { + "location": "[resourceGroup().location]", + "tenantId": "[subscription().tenantId]", + "webAppName": "[uniqueString(resourceGroup().id, '0c61332e-646a-4e96-b9ff-ade92112644d')]", + "keyVaultName": "[variables('webAppName')]", + "keyVaultSecrets": [ + { + "name": "EASYCONFIG--KeyVaultExample1", + "value": "First vaule from KeyVault" + }, + { + "name": "EASYCONFIG--KeyVaultExample2", + "value": "Second vaule from KeyVault" + } + ] + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2018-02-14", + "name": "[variables('keyVaultName')]", + "location": "[variables('location')]", + "tags": { + "displayName": "KeyVault", + "Deploymentmethod": "ARM" + }, + "dependsOn": [ + "[resourceId('Microsoft.Web/sites', variables('webAppName'))]" + ], + "properties": { + "enabledForDeployment": false, + "enabledForTemplateDeployment": false, + "enabledForDiskEncryption": false, + "tenantId": "[variables('tenantId')]", + "accessPolicies": [ + { + "tenantId": "[variables('tenantId')]", + "objectId": "[reference(resourceId('Microsoft.Web/sites', variables('webAppName')), '2019-08-01', 'Full').identity.principalId]", + "permissions": { + "secrets": [ + "get", + "list" + ] + } + } + ], + "sku": { + "name": "Standard", + "family": "A" + }, + "networkAcls": { + "defaultAction": "Allow", + "bypass": "AzureServices", + "ipRules": [ + ], + "virtualNetworkRules": [ + ] + } + } + }, + { + "copy": { + "name": "secrets", + "count": "[length(variables('keyVaultSecrets'))]" + }, + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2021-04-01-preview", + "name": "[format('{0}/{1}', variables('keyVaultName'), variables('keyVaultSecrets')[copyIndex()].name)]", + "tags": { + "displayName": "KeyVault - Secrets" + }, + "properties": { + "value": "[variables('keyVaultSecrets')[copyIndex()].value]" + }, + "dependsOn": [ + "[resourceId('Microsoft.KeyVault/vaults', variables('keyVaultName'))]" + ] + }, + { + "apiVersion": "2020-06-01", + "type": "Microsoft.Web/serverfarms", + "name": "[variables('webAppName')]", + "location": "[variables('location')]", + "tags": { + "displayName": "App Service Plan" + }, + "sku": { + "name": "F1" + } + }, + { + "apiVersion": "2020-06-01", + "type": "Microsoft.Web/sites", + "name": "[variables('webAppName')]", + "location": "[variables('location')]", + "tags": { + "displayName": "Web App" + }, + "dependsOn": [ + "[resourceId('Microsoft.Web/serverfarms', variables('webAppName'))]" + ], + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('webAppName'))]" + }, + "resources": [ + { + "type": "config", + "name": "appsettings", + "apiVersion": "2016-08-01", + "tags": { + "displayName": "Application Settings" + }, + "dependsOn": [ + "[resourceId('Microsoft.Web/sites', variables('webAppName'))]" + ], + "properties": { + "KeyVault__Uri": "[reference(resourceId('Microsoft.KeyVault/vaults', variables('keyVaultName')), '2019-09-01').vaultUri]", + "EASYCONFIG__apiUri": "[concat('https://', reference(resourceId('Microsoft.Web/sites', variables('webAppName')), '2019-08-01').defaultHostName, '/api')]" + } + }, + { + "type": "siteextensions", + "name": "EasyConfig.SiteExtension", + "apiVersion": "2021-02-01", + "tags": { + "displayName": "Site Extension: EasyConfig" + }, + "dependsOn": [ + "[resourceId('Microsoft.Web/sites', variables('webAppName'))]" + ], + "properties": { + } + } + ] + } + ] +} diff --git a/global.json b/global.json index 05d0ae3..e52d340 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "3.1.201" + "version": "6.0.101" } } diff --git a/pipelines/EasyConfig.yaml b/pipelines/EasyConfig.yaml index 122d684..8ced271 100644 --- a/pipelines/EasyConfig.yaml +++ b/pipelines/EasyConfig.yaml @@ -8,7 +8,6 @@ variables: Project: "$(Build.SourcesDirectory)/src/EasyConfig.SiteExtension/EasyConfig.SiteExtension.csproj" ProjectFolder: "$(Build.SourcesDirectory)/src/EasyConfig.SiteExtension" NugetFolder: "$(Build.SourcesDirectory)/src/EasyConfig.SiteExtension.NuGet" - GitVersion: "5.1.2" name: $(Year:yy)$(DayOfYear)$(Rev:rr) @@ -20,19 +19,19 @@ steps: useGlobalJson: true - task: DotNetCoreCLI@2 - displayName: 'Install GitVersion: $(GitVersion)' + displayName: 'Restore dotnet tools' inputs: - command: custom + command: 'custom' custom: 'tool' - arguments: 'install GitVersion.Tool --version $(GitVersion) --tool-path ./pipelines/tools/gitversion' + arguments: 'restore' - - task: PowerShell@2 - displayName: Run GitVersion + - task: DotNetCoreCLI@2 + displayName: 'Run GitVersion' inputs: - targetType: "filePath" - filePath: "pipelines/tools/Run-GitVersion.ps1" - arguments: '-Verbose' - workingDirectory: "$(ProjectFolder)" + command: 'custom' + custom: 'tool' + arguments: 'run dotnet-gitversion /updateassemblyinfo AssemblyInfo.cs /ensureassemblyinfo /output buildserver /nofetch' + workingDirectory: '$(ProjectFolder)' - task: DotNetCoreCLI@2 displayName: Restore @@ -73,7 +72,7 @@ steps: configuration: '$(BuildConfiguration)' nobuild: true versioningScheme: 'byEnvVar' - versionEnvVar: 'GitVersion_NuGetVersion' + versionEnvVar: 'GitVersion.NuGetVersion' buildProperties: '' verbosityPack: 'Normal' diff --git a/pipelines/tools/Run-GitVersion.ps1 b/pipelines/tools/Run-GitVersion.ps1 deleted file mode 100644 index 66d636b..0000000 --- a/pipelines/tools/Run-GitVersion.ps1 +++ /dev/null @@ -1,25 +0,0 @@ -[CmdletBinding(DefaultParameterSetName = 'None')] -Param() - -Begin { - Write-Verbose "Entering script Run-GitVersion.ps1" -} - -Process { - Write-Verbose "Running GitVersion ..." - $gitversionoutput = & "$PSScriptRoot/gitversion/dotnet-gitversion*" $PWD /updateassemblyinfo AssemblyInfo.cs /ensureassemblyinfo /output json - Write-Verbose " Done" - - $jsonObj = "$gitversionoutput" | ConvertFrom-Json - - Write-Verbose "Writing variables ..." - foreach ($property in $jsonObj.PSObject.Properties) { - Write-Verbose " GitVersion_$($property.Name): $($property.Value)" - Write-Output "##vso[task.setvariable variable=GitVersion_$($property.Name);]$($property.Value)" - } - Write-Verbose " Done" -} - -End { - Write-Verbose "Leaving script Run-GitVersion.ps1" -} diff --git a/publish.ps1 b/publish.ps1 index f2d88fc..dd0ff78 100644 --- a/publish.ps1 +++ b/publish.ps1 @@ -3,11 +3,7 @@ $dir = Split-Path $scriptpath $extensionfolder = $dir + "/" + "src/EasyConfig.SiteExtension" $nugetfolder = $dir + "/" + "src/EasyConfig.SiteExtension.NuGet" -dotnet publish "$extensionfolder/EasyConfig.SiteExtension.csproj" --output "$nugetfolder/content/" -f netcoreapp3.1 -c Release +dotnet publish "$extensionfolder/EasyConfig.SiteExtension.csproj" --output "$nugetfolder/content/" -f "net6.0" -c Release Copy-Item "$nugetfolder/applicationHost.xdt" "$nugetfolder/content/" dotnet pack "$extensionfolder/EasyConfig.SiteExtension.csproj" /p:PackageVersion=1.1.1 -o ./output -c Release --no-build -# $nupkgfilename = @(Get-Childitem -path ./output/*.nupkg)[0].Name - -#dotnet nuget push $nupkgfilename -k $env:NugetKey -s https://api.nuget.org/v3/index.json -#dotnet nuget push $nupkgfilename -k $env:NugetKey -s https://www.myget.org/F/mmercan/api/v3/index.json diff --git a/src/EasyConfig.SiteExtension/AppConfiguration/KeyVaultAppConfiguration.cs b/src/EasyConfig.SiteExtension/AppConfiguration/KeyVaultAppConfiguration.cs new file mode 100644 index 0000000..814df35 --- /dev/null +++ b/src/EasyConfig.SiteExtension/AppConfiguration/KeyVaultAppConfiguration.cs @@ -0,0 +1,55 @@ +namespace EasyConfig.SiteExtension.AppConfiguration; + +using Azure.Identity; + +internal static class KeyVaultAppConfiguration +{ + internal static ConfigurationManager AddEasyConfigAzureKeyVault(this ConfigurationManager configurationManager) + { + var uriList = new List(); + + var uriString = configurationManager["KeyVault:Uri"]; + if (!string.IsNullOrWhiteSpace(uriString)) + { + uriList.Add(uriString); + } + + // This will happen when the config looks like: + // { + // "KeyVault":{ + // "Uri": [ + // "sample1.vault.azure.net/", + // "sample2.vault.azure.net/" + // ] + // } + // } + else + { + uriList = configurationManager.GetSection("KeyVault:Uri").Get>(); + } + + + // Add KeyVault only if the uri is not empty + if (uriList?.Count > 0) + { + foreach (var uri in uriList) + { + if (Uri.TryCreate(uri, UriKind.Absolute, out var keyvaultUri)) + { + // Add Key Vault to configuration pipeline + _ = configurationManager.AddAzureKeyVault( + keyvaultUri, + new DefaultAzureCredential(), + new PrefixKeyVaultSecretManager() + ); + } + else + { + // TODO: Write Log + } + + } + } + return configurationManager; + } +} diff --git a/src/EasyConfig.SiteExtension/Controllers/ConfigController.cs b/src/EasyConfig.SiteExtension/Controllers/ConfigController.cs index d0fabb9..4722ddc 100644 --- a/src/EasyConfig.SiteExtension/Controllers/ConfigController.cs +++ b/src/EasyConfig.SiteExtension/Controllers/ConfigController.cs @@ -1,70 +1,66 @@ -namespace EasyConfig.SiteExtension.Controllers +namespace EasyConfig.SiteExtension.Controllers; + +using Microsoft.AspNetCore.Mvc; +using System.Text.Json.Nodes; + +[ApiController] +public class ConfigController : ControllerBase { - using System.Collections.Generic; - using System.Linq; - using System.Text.Json; - using Microsoft.AspNetCore.Mvc; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.Logging; - using Newtonsoft.Json.Linq; + private readonly ILogger logger; + private readonly IConfigurationSection easyconfig; - [ApiController] - public class ConfigController : ControllerBase + public ConfigController( + IConfiguration configuration, + ILogger logger + ) { - private readonly ILogger logger; - private readonly IConfigurationSection easyconfig; + this.easyconfig = configuration.GetSection("EASYCONFIG"); + this.logger = logger; + } - public ConfigController( - IConfiguration configuration, - ILogger logger - ) - { - this.easyconfig = configuration.GetSection("EASYCONFIG"); - this.logger = logger; - } + [Route("")] + [Route("config")] + [Route("config.json")] + [Route("environment")] + [Route("environment.json")] + public JsonNode? GetEnvironment() + { + Logging.Controllers.Config.GetConfigRequested(this.logger); - [Route("")] - [Route("environment")] - [Route("environment.json")] - public JToken GetEnvironment() - { - this.logger.LogInformation("Get Environment Config called."); + return Serialize(this.easyconfig); + } - return this.Serialize(this.easyconfig); - } + private static JsonNode? Serialize(IConfiguration config) + { + var childs = config.GetChildren(); + var isArray = childs.Any() && childs.All(child => int.TryParse(child.Key, out var number)); - private JToken Serialize(IConfiguration config) + if (isArray) { - var childs = config.GetChildren(); - var isArray = childs.Any() && childs.All(child => int.TryParse(child.Key, out var number)); + var obj = new JsonArray(); - if (isArray) + foreach (var child in childs) { - var obj = new JArray(); - - foreach (var child in childs) - { - obj.Add(this.Serialize(child)); - } - - return obj; + obj.Add(Serialize(child)); } - else - { - var obj = new JObject(); - foreach (var child in childs) - { - obj.Add(child.Key, this.Serialize(child)); - } + return obj; + } + else + { + var obj = new JsonObject(); - if (!obj.HasValues && config is IConfigurationSection section) - { - return new JValue(section.Value); - } + foreach (var child in childs) + { + obj.Add(child.Key, Serialize(child)); + } - return obj; + if (obj.Count == 0 && config is IConfigurationSection section) + { + return JsonValue.Create(section.Value); } + + return obj; } } } diff --git a/src/EasyConfig.SiteExtension/EasyConfig.SiteExtension.csproj b/src/EasyConfig.SiteExtension/EasyConfig.SiteExtension.csproj index b91eafd..cfb8fe5 100644 --- a/src/EasyConfig.SiteExtension/EasyConfig.SiteExtension.csproj +++ b/src/EasyConfig.SiteExtension/EasyConfig.SiteExtension.csproj @@ -2,11 +2,14 @@ Exe - netcoreapp3.1 - true + net6.0 + enable + enable + + true false InProcess @@ -17,9 +20,8 @@ - - - + + diff --git a/src/EasyConfig.SiteExtension/Logging/Logging.cs b/src/EasyConfig.SiteExtension/Logging/Logging.cs new file mode 100644 index 0000000..fdb996e --- /dev/null +++ b/src/EasyConfig.SiteExtension/Logging/Logging.cs @@ -0,0 +1,22 @@ +namespace EasyConfig.SiteExtension; + +using Microsoft.Extensions.Logging; + +// For more info have a look here: +// https://docs.microsoft.com/en-us/dotnet/core/extensions/logger-message-generator + +public static partial class Logging +{ + public static partial class Controllers + { + public static partial class Config + { + [LoggerMessage( + EventId = 1, + Level = LogLevel.Information, + Message = "Get Environment Config called." + )] + internal static partial void GetConfigRequested(ILogger logger); + } + } +} \ No newline at end of file diff --git a/src/EasyConfig.SiteExtension/PrefixKeyVaultSecretManager.cs b/src/EasyConfig.SiteExtension/PrefixKeyVaultSecretManager.cs index 5b6728c..1c6cf1d 100644 --- a/src/EasyConfig.SiteExtension/PrefixKeyVaultSecretManager.cs +++ b/src/EasyConfig.SiteExtension/PrefixKeyVaultSecretManager.cs @@ -1,34 +1,33 @@ -namespace EasyConfig.SiteExtension +namespace EasyConfig.SiteExtension; + +using Microsoft.Extensions.Configuration; +using Azure.Extensions.AspNetCore.Configuration.Secrets; +using Azure.Security.KeyVault.Secrets; + +public class PrefixKeyVaultSecretManager : KeyVaultSecretManager { - using Microsoft.Azure.KeyVault.Models; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.Configuration.AzureKeyVault; + private readonly string prefix; + private readonly bool removePrefix; - public class PrefixKeyVaultSecretManager : IKeyVaultSecretManager + public PrefixKeyVaultSecretManager( + string prefix = "EASYCONFIG", + bool removePrefix = false + ) { - private readonly string prefix; - private readonly bool removePrefix; + this.prefix = $"{prefix}--"; + this.removePrefix = removePrefix; + } - public PrefixKeyVaultSecretManager( - string prefix = "EASYCONFIG", - bool removePrefix = false - ) - { - this.prefix = $"{prefix}--"; - this.removePrefix = removePrefix; - } + public override bool Load(SecretProperties secret) => secret.Name.StartsWith(this.prefix, StringComparison.InvariantCultureIgnoreCase); - public bool Load(SecretItem secret) => secret.Identifier.Name.StartsWith(this.prefix); + public override string GetKey(KeyVaultSecret secret) + { + var secretIdentifier = secret.Name; - public string GetKey(SecretBundle secret) + if (this.removePrefix) { - var secretIdentifier = secret.SecretIdentifier.Name; - - if (this.removePrefix) - { - secretIdentifier = secretIdentifier.Substring(this.prefix.Length); - } - return secretIdentifier.Replace("--", ConfigurationPath.KeyDelimiter); + secretIdentifier = secretIdentifier[this.prefix.Length..]; } + return secretIdentifier.Replace("--", ConfigurationPath.KeyDelimiter); } } diff --git a/src/EasyConfig.SiteExtension/Program.cs b/src/EasyConfig.SiteExtension/Program.cs index c59c69b..e04a9d6 100644 --- a/src/EasyConfig.SiteExtension/Program.cs +++ b/src/EasyConfig.SiteExtension/Program.cs @@ -1,78 +1,19 @@ -namespace EasyConfig.SiteExtension -{ - using System.Collections.Generic; - using Microsoft.AspNetCore.Hosting; - using Microsoft.Azure.KeyVault; - using Microsoft.Azure.Services.AppAuthentication; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.Hosting; +using EasyConfig.SiteExtension.AppConfiguration; - public class Program - { - public static void Main(string[] args) => CreateHostBuilder(args).Build().Run(); +var builder = WebApplication.CreateBuilder(args); - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup()) - .ConfigureAppConfiguration( - (ctx, builder) => - { - //Build the config from sources we have - var config = builder.Build(); +builder.Configuration.AddEasyConfigAzureKeyVault(); - var uriList = new List(); +// Add services to the container. +builder.Services.AddControllers().AddJsonOptions( + options => options.JsonSerializerOptions.WriteIndented = true +); - // Get the uri for the Vault from configuration - // Try to get a string from configutation - // This will happen when the config looks like: - // { - // "KeyVault":{ - // "Uri": "sample.vault.azure.net/" - // } - // } - var uriString = config["KeyVault:Uri"]; - if (!string.IsNullOrWhiteSpace(uriString)) - { - uriList.Add(uriString); - } - // This will happen when the config looks like: - // { - // "KeyVault":{ - // "Uri": [ - // "sample1.vault.azure.net/", - // "sample2.vault.azure.net/" - // ] - // } - // } - else - { - uriList = config.GetSection("KeyVault:Uri").Get>(); - } +var app = builder.Build(); +// Configure the HTTP request pipeline. +app.UseHttpsRedirection(); - // Add KeyVault only if the uri is not empty - if (uriList?.Count > 0) - { - // Create Managed Service Identity token provider - var azureServiceTokenProvider = new AzureServiceTokenProvider(); +app.MapControllers(); - // Create the Key Vault client - var keyVaultClient = new KeyVaultClient( - new KeyVaultClient.AuthenticationCallback( - azureServiceTokenProvider.KeyVaultTokenCallback) - ); - - foreach (var uri in uriList) - { - // Add Key Vault to configuration pipeline - _ = builder.AddAzureKeyVault( - uri, - keyVaultClient, - new PrefixKeyVaultSecretManager() - ); - } - } - } - ); - } -} +app.Run(); diff --git a/src/EasyConfig.SiteExtension/Properties/launchSettings.json b/src/EasyConfig.SiteExtension/Properties/launchSettings.json index bad2024..cea23b6 100644 --- a/src/EasyConfig.SiteExtension/Properties/launchSettings.json +++ b/src/EasyConfig.SiteExtension/Properties/launchSettings.json @@ -1,27 +1,28 @@ { - "$schema": "http://json.schemastore.org/launchsettings.json", + "$schema": "https://json.schemastore.org/launchsettings.json", "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:13192", - "sslPort": 44390 + "applicationUrl": "http://localhost:55626", + "sslPort": 44366 } }, "profiles": { - "IIS Express": { - "commandName": "IISExpress", + "EasyConfig": { + "commandName": "Project", + "dotnetRunMessages": true, "launchBrowser": true, - "launchUrl": "weatherforecast", + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7186;http://localhost:5229", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, - "EasyConfig": { - "commandName": "Project", + "IIS Express": { + "commandName": "IISExpress", "launchBrowser": true, - "launchUrl": "weatherforecast", - "applicationUrl": "https://localhost:5001;http://localhost:5000", + "launchUrl": "swagger", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/src/EasyConfig.SiteExtension/Startup.cs b/src/EasyConfig.SiteExtension/Startup.cs deleted file mode 100644 index 2253ace..0000000 --- a/src/EasyConfig.SiteExtension/Startup.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace EasyConfig.SiteExtension -{ - using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Hosting; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Hosting; - - public class Startup - { - public Startup(IConfiguration configuration) => this.Configuration = configuration; - - public IConfiguration Configuration { get; } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - _ = services.AddOptions(); - _ = services.AddControllers().AddNewtonsoftJson(); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - _ = app.UseDeveloperExceptionPage(); - } - - _ = app.UseHttpsRedirection(); - - _ = app.UseRouting(); - - _ = app.UseAuthorization(); - - _ = app.UseEndpoints(endpoints => endpoints.MapControllers()); - } - } -} diff --git a/src/EasyConfig.SiteExtension/appsettings.Development.json b/src/EasyConfig.SiteExtension/appsettings.Development.json new file mode 100644 index 0000000..ab79508 --- /dev/null +++ b/src/EasyConfig.SiteExtension/appsettings.Development.json @@ -0,0 +1,14 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "EASYCONFIG": { + "section": { + "setting": "Hello from section" + }, + "setting": "Hello World!" + } +} diff --git a/src/EasyConfig.SiteExtension/appsettings.json b/src/EasyConfig.SiteExtension/appsettings.json index d9d9a9b..10f68b8 100644 --- a/src/EasyConfig.SiteExtension/appsettings.json +++ b/src/EasyConfig.SiteExtension/appsettings.json @@ -2,8 +2,7 @@ "Logging": { "LogLevel": { "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" + "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" From 927c1a32cf5e4d56cadaa955e2556d2dd773eef2 Mon Sep 17 00:00:00 2001 From: paule96 Date: Mon, 10 Jan 2022 11:40:24 +0000 Subject: [PATCH 2/5] fixes the global json --- global.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/global.json b/global.json index e52d340..1b8195c 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,6 @@ { "sdk": { - "version": "6.0.101" + "version": "6.0.100", + "rollForward": "latestMinor" } } From 353fb75f6720376dce4b657fc7751e42a424a1ff Mon Sep 17 00:00:00 2001 From: paule96 Date: Mon, 10 Jan 2022 11:40:42 +0000 Subject: [PATCH 3/5] add keyvault docs --- docs/users/Index.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/users/Index.md b/docs/users/Index.md index 3389b34..8f753de 100644 --- a/docs/users/Index.md +++ b/docs/users/Index.md @@ -48,8 +48,14 @@ All other *Application settings* which does not start with `EASYCONFIG__` will b ### Azure KeyVault An other way to store the Settings is using an Azure KeyVault. +To use the KeyVault you simply use in the app settings the following line configuration to add it: -coming soon ™ +``` +KeyVault__Uri +``` + +And set it to the uri of your keyvault. Then you must allow the managed identity of your webapp to read the keyvault. +Then it will be use all the secrets in the KeyVault that start with `EASYCONFIG--`. ### Using the endpoint From 6e86275646a407f81dbdf12171b5d0c7e5b95009 Mon Sep 17 00:00:00 2001 From: paule96 Date: Mon, 10 Jan 2022 11:42:13 +0000 Subject: [PATCH 4/5] fixes docs --- docs/users/Index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/users/Index.md b/docs/users/Index.md index 8f753de..b79c9c0 100644 --- a/docs/users/Index.md +++ b/docs/users/Index.md @@ -5,7 +5,7 @@ ## Requirements - Extensions can only be installed in Windows basesd App Services at the moment. -- The extension is written in c# using dotnet core 3.1 as self-contained application, so there should be **no** dependency to installed frameworks +- The extension is written in c# using dotnet 6.0 as self-contained application, so there should be **no** dependency to installed frameworks ## Installation From 1b7b4319aa9311ab18eb67a63f2d0f6ffc7a33a7 Mon Sep 17 00:00:00 2001 From: paule96 Date: Mon, 10 Jan 2022 11:55:24 +0000 Subject: [PATCH 5/5] add dev container --- .devcontainer/Dockerfile | 16 +++++++++ .devcontainer/devcontainer.json | 58 +++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..aaebc05 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,16 @@ +# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.209.6/containers/dotnet/.devcontainer/base.Dockerfile + +# [Choice] .NET version: 6.0, 5.0, 3.1, 6.0-bullseye, 5.0-bullseye, 3.1-bullseye, 6.0-focal, 5.0-focal, 3.1-focal +ARG VARIANT="6.0-bullseye-slim" +FROM mcr.microsoft.com/vscode/devcontainers/dotnet:0-${VARIANT} + +# [Choice] Node.js version: none, lts/*, 16, 14, 12, 10 +ARG NODE_VERSION="none" +RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi + +# [Optional] Uncomment this section to install additional OS packages. +# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ +# && apt-get -y install --no-install-recommends + +# [Optional] Uncomment this line to install global node packages. +# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g " 2>&1 \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..36e5476 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,58 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: +// https://github.com/microsoft/vscode-dev-containers/tree/v0.209.6/containers/dotnet +{ + "name": "C# (.NET)", + "build": { + "dockerfile": "Dockerfile", + "args": { + // Update 'VARIANT' to pick a .NET Core version: 3.1, 5.0, 6.0 + // Append -bullseye or -focal to pin to an OS version. + "VARIANT": "6.0", + // Options + "NODE_VERSION": "lts/*" + } + }, + + // Set *default* container specific settings.json values on container create. + "settings": {}, + + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "ms-dotnettools.csharp" + ], + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [5000, 5001], + + // [Optional] To reuse of your local HTTPS dev cert: + // + // 1. Export it locally using this command: + // * Windows PowerShell: + // dotnet dev-certs https --trust; dotnet dev-certs https -ep "$env:USERPROFILE/.aspnet/https/aspnetapp.pfx" -p "SecurePwdGoesHere" + // * macOS/Linux terminal: + // dotnet dev-certs https --trust; dotnet dev-certs https -ep "${HOME}/.aspnet/https/aspnetapp.pfx" -p "SecurePwdGoesHere" + // + // 2. Uncomment these 'remoteEnv' lines: + // "remoteEnv": { + // "ASPNETCORE_Kestrel__Certificates__Default__Password": "SecurePwdGoesHere", + // "ASPNETCORE_Kestrel__Certificates__Default__Path": "/home/vscode/.aspnet/https/aspnetapp.pfx", + // }, + // + // 3. Do one of the following depending on your scenario: + // * When using GitHub Codespaces and/or Remote - Containers: + // 1. Start the container + // 2. Drag ~/.aspnet/https/aspnetapp.pfx into the root of the file explorer + // 3. Open a terminal in VS Code and run "mkdir -p /home/vscode/.aspnet/https && mv aspnetapp.pfx /home/vscode/.aspnet/https" + // + // * If only using Remote - Containers with a local container, uncomment this line instead: + // "mounts": [ "source=${env:HOME}${env:USERPROFILE}/.aspnet/https,target=/home/vscode/.aspnet/https,type=bind" ], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "dotnet restore", + + // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. + "remoteUser": "vscode", + "features": { + "powershell": "latest" + } +}