diff --git a/.autover/autover.json b/.autover/autover.json new file mode 100644 index 0000000..cc1cdd2 --- /dev/null +++ b/.autover/autover.json @@ -0,0 +1,11 @@ +{ + "Projects": [ + { + "Name": "Amazon.Extensions.Configuration.SystemsManager", + "Path": "src/Amazon.Extensions.Configuration.SystemsManager/Amazon.Extensions.Configuration.SystemsManager.csproj" + } + ], + "UseCommitsForChangelog": false, + "DefaultIncrementType": "Patch", + "ChangeFilesDetermineIncrementType": true +} \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 9678d89..56d895f 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -12,6 +12,14 @@ body: description: What is the problem? A clear and concise description of the bug. validations: required: true + - type: checkboxes + id: regression + attributes: + label: Regression Issue + description: What is a regression? If it worked in a previous version but doesn't in the latest version, it's considered a regression. In this case, please provide specific version number in the report. + options: + - label: Select this option if this issue appears to be a regression. + required: false - type: textarea id: expected attributes: diff --git a/.github/workflows/aws-ci.yml b/.github/workflows/aws-ci.yml new file mode 100644 index 0000000..f35e624 --- /dev/null +++ b/.github/workflows/aws-ci.yml @@ -0,0 +1,46 @@ +name: AWS CI + +on: + workflow_dispatch: + pull_request: + branches: + - master + - dev + - 'feature/**' + +permissions: + id-token: write + +jobs: + run-ci: + runs-on: ubuntu-latest + steps: + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 #v4 + with: + role-to-assume: ${{ secrets.CI_MAIN_TESTING_ACCOUNT_ROLE_ARN }} + role-duration-seconds: 7200 + aws-region: us-west-2 + - name: Invoke Load Balancer Lambda + id: lambda + shell: pwsh + run: | + aws lambda invoke response.json --function-name "${{ secrets.CI_TESTING_LOAD_BALANCER_LAMBDA_NAME }}" --cli-binary-format raw-in-base64-out --payload '{"Roles": "${{ secrets.CI_TEST_RUNNER_ACCOUNT_ROLES }}", "ProjectName": "${{ secrets.CI_TESTING_CODE_BUILD_PROJECT_NAME }}", "Branch": "${{ github.sha }}"}' + $roleArn=$(cat ./response.json) + "roleArn=$($roleArn -replace '"', '')" >> $env:GITHUB_OUTPUT + - name: Configure Test Runner Credentials + uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 #v4 + with: + role-to-assume: ${{ steps.lambda.outputs.roleArn }} + role-duration-seconds: 7200 + aws-region: us-west-2 + - name: Run Tests on AWS + id: codebuild + uses: aws-actions/aws-codebuild-run-build@v1 + with: + project-name: ${{ secrets.CI_TESTING_CODE_BUILD_PROJECT_NAME }} + - name: CodeBuild Link + shell: pwsh + run: | + $buildId = "${{ steps.codebuild.outputs.aws-build-id }}" + echo $buildId \ No newline at end of file diff --git a/.github/workflows/create-release-pr.yml b/.github/workflows/create-release-pr.yml new file mode 100644 index 0000000..2591e5f --- /dev/null +++ b/.github/workflows/create-release-pr.yml @@ -0,0 +1,101 @@ +# This GitHub Workflow will create a new release branch that contains the updated C# project versions and changelog. +# The workflow will also create a PR that targets `dev` from the release branch. +name: Create Release PR + +# This workflow is manually triggered when in preparation for a release. The workflow should be dispatched from the `dev` branch. +on: + workflow_dispatch: + inputs: + OVERRIDE_VERSION: + description: "Override Version" + type: string + required: false + +permissions: + id-token: write + +jobs: + release-pr: + name: Release PR + runs-on: ubuntu-latest + + env: + INPUT_OVERRIDE_VERSION: ${{ github.event.inputs.OVERRIDE_VERSION }} + + steps: + # Assume an AWS Role that provides access to the Access Token + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@8c3f20df09ac63af7b3ae3d7c91f105f857d8497 #v4 + with: + role-to-assume: ${{ secrets.RELEASE_WORKFLOW_ACCESS_TOKEN_ROLE_ARN }} + aws-region: us-west-2 + # Retrieve the Access Token from Secrets Manager + - name: Retrieve secret from AWS Secrets Manager + uses: aws-actions/aws-secretsmanager-get-secrets@v2 + with: + secret-ids: | + AWS_SECRET, ${{ secrets.RELEASE_WORKFLOW_ACCESS_TOKEN_NAME }} + parse-json-secrets: true + # Checkout a full clone of the repo + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: '0' + token: ${{ env.AWS_SECRET_TOKEN }} + # Install .NET8 which is needed for AutoVer + - name: Setup .NET 8.0 + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + # Install AutoVer to automate versioning and changelog creation + - name: Install AutoVer + run: dotnet tool install --global AutoVer --version 0.0.21 + # Set up a git user to be able to run git commands later on + - name: Setup Git User + run: | + git config --global user.email "github-aws-sdk-dotnet-automation@amazon.com" + git config --global user.name "aws-sdk-dotnet-automation" + # Create the release branch which will contain the version changes and updated changelog + - name: Create Release Branch + id: create-release-branch + run: | + branch=releases/next-release + git checkout -b $branch + echo "BRANCH=$branch" >> $GITHUB_OUTPUT + # Update the version of projects based on the change files + - name: Increment Version + run: autover version + if: env.INPUT_OVERRIDE_VERSION == '' + # Update the version of projects based on the override version + - name: Increment Version + run: autover version --use-version "$INPUT_OVERRIDE_VERSION" + if: env.INPUT_OVERRIDE_VERSION != '' + # Update the changelog based on the change files + - name: Update Changelog + run: autover changelog + # Push the release branch up as well as the created tag + - name: Push Changes + run: | + branch=${{ steps.create-release-branch.outputs.BRANCH }} + git push origin $branch + git push origin $branch --tags + # Get the release name that will be used to create a PR + - name: Read Release Name + id: read-release-name + run: | + version=$(autover changelog --release-name) + echo "VERSION=$version" >> $GITHUB_OUTPUT + # Get the changelog that will be used to create a PR + - name: Read Changelog + id: read-changelog + run: | + changelog=$(autover changelog --output-to-console) + echo "CHANGELOG<> "$GITHUB_OUTPUT" + # Create the Release PR and label it + - name: Create Pull Request + env: + GITHUB_TOKEN: ${{ env.AWS_SECRET_TOKEN }} + run: | + pr_url="$(gh pr create --title "${{ steps.read-release-name.outputs.VERSION }}" --body "${{ steps.read-changelog.outputs.CHANGELOG }}" --base dev --head ${{ steps.create-release-branch.outputs.BRANCH }})" + gh label create "Release PR" --description "A Release PR that includes versioning and changelog changes" -c "#FF0000" -f + gh pr edit $pr_url --add-label "Release PR" \ No newline at end of file diff --git a/.github/workflows/issue-regression-labeler.yml b/.github/workflows/issue-regression-labeler.yml new file mode 100644 index 0000000..bd00071 --- /dev/null +++ b/.github/workflows/issue-regression-labeler.yml @@ -0,0 +1,32 @@ +# Apply potential regression label on issues +name: issue-regression-label +on: + issues: + types: [opened, edited] +jobs: + add-regression-label: + runs-on: ubuntu-latest + permissions: + issues: write + steps: + - name: Fetch template body + id: check_regression + uses: actions/github-script@v7 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TEMPLATE_BODY: ${{ github.event.issue.body }} + with: + script: | + const regressionPattern = /\[x\] Select this option if this issue appears to be a regression\./i; + const template = `${process.env.TEMPLATE_BODY}` + const match = regressionPattern.test(template); + core.setOutput('is_regression', match); + - name: Manage regression label + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + if [ "${{ steps.check_regression.outputs.is_regression }}" == "true" ]; then + gh issue edit ${{ github.event.issue.number }} --add-label "potential-regression" -R ${{ github.repository }} + else + gh issue edit ${{ github.event.issue.number }} --remove-label "potential-regression" -R ${{ github.repository }} + fi diff --git a/.github/workflows/sync-main-dev.yml b/.github/workflows/sync-main-dev.yml new file mode 100644 index 0000000..e7e4a84 --- /dev/null +++ b/.github/workflows/sync-main-dev.yml @@ -0,0 +1,137 @@ +# This GitHub Workflow is designed to run automatically after the Release PR, which was created by the `Create Release PR` workflow, is closed. +# This workflow has 2 jobs. One will run if the `Release PR` is successfully merged, indicating that a release should go out. +# The other will run if the `Release PR` was closed and a release is not intended to go out. +name: Sync 'dev' and 'master' + +# The workflow will automatically be triggered when any PR is closed. +on: + pull_request: + types: [closed] + +permissions: + contents: write + id-token: write + +jobs: + # This job will check if the PR was successfully merged, it's source branch is `releases/next-release` and target branch is `dev`. + # This indicates that the merged PR was the `Release PR`. + # This job will synchronize `dev` and `master`, create a GitHub Release and delete the `releases/next-release` branch. + sync-dev-and-main: + name: Sync dev and master + if: | + github.event.pull_request.merged == true && + github.event.pull_request.head.ref == 'releases/next-release' && + github.event.pull_request.base.ref == 'dev' + runs-on: ubuntu-latest + steps: + # Assume an AWS Role that provides access to the Access Token + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@8c3f20df09ac63af7b3ae3d7c91f105f857d8497 #v4 + with: + role-to-assume: ${{ secrets.RELEASE_WORKFLOW_ACCESS_TOKEN_ROLE_ARN }} + aws-region: us-west-2 + # Retrieve the Access Token from Secrets Manager + - name: Retrieve secret from AWS Secrets Manager + uses: aws-actions/aws-secretsmanager-get-secrets@v2 + with: + secret-ids: | + AWS_SECRET, ${{ secrets.RELEASE_WORKFLOW_ACCESS_TOKEN_NAME }} + parse-json-secrets: true + # Checkout a full clone of the repo + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: dev + fetch-depth: 0 + token: ${{ env.AWS_SECRET_TOKEN }} + # Install .NET8 which is needed for AutoVer + - name: Setup .NET 8.0 + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + # Install AutoVer which is needed to retrieve information about the current release. + - name: Install AutoVer + run: dotnet tool install --global AutoVer --version 0.0.21 + # Set up a git user to be able to run git commands later on + - name: Setup Git User + run: | + git config --global user.email "github-aws-sdk-dotnet-automation@amazon.com" + git config --global user.name "aws-sdk-dotnet-automation" + # Retrieve the release name which is needed for the GitHub Release + - name: Read Release Name + id: read-release-name + run: | + version=$(autover changelog --release-name) + echo "VERSION=$version" >> $GITHUB_OUTPUT + # Retrieve the tag name which is needed for the GitHub Release + - name: Read Tag Name + id: read-tag-name + run: | + tag=$(autover changelog --tag-name) + echo "TAG=$tag" >> $GITHUB_OUTPUT + # Retrieve the changelog which is needed for the GitHub Release + - name: Read Changelog + id: read-changelog + run: | + changelog=$(autover changelog --output-to-console) + echo "CHANGELOG<> "$GITHUB_OUTPUT" + # Merge dev into master in order to synchronize the 2 branches + - name: Merge dev to master + run: | + git fetch origin + git checkout master + git merge dev + git push origin master + # Create the GitHub Release + - name: Create GitHub Release + env: + GITHUB_TOKEN: ${{ env.AWS_SECRET_TOKEN }} + run: | + gh release create "${{ steps.read-tag-name.outputs.TAG }}" --title "${{ steps.read-release-name.outputs.VERSION }}" --notes "${{ steps.read-changelog.outputs.CHANGELOG }}" + # Delete the `releases/next-release` branch + - name: Clean up + run: | + git fetch origin + git push origin --delete releases/next-release + # This job will check if the PR was closed, it's source branch is `releases/next-release` and target branch is `dev`. + # This indicates that the closed PR was the `Release PR`. + # This job will delete the tag created by AutoVer and the release branch. + clean-up-closed-release: + name: Clean up closed release + if: | + github.event.pull_request.merged == false && + github.event.pull_request.head.ref == 'releases/next-release' && + github.event.pull_request.base.ref == 'dev' + runs-on: ubuntu-latest + steps: + # Checkout a full clone of the repo + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: releases/next-release + fetch-depth: 0 + # Install .NET8 which is needed for AutoVer + - name: Setup .NET 8.0 + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + # Install AutoVer which is needed to retrieve information about the current release. + - name: Install AutoVer + run: dotnet tool install --global AutoVer --version 0.0.21 + # Set up a git user to be able to run git commands later on + - name: Setup Git User + run: | + git config --global user.email "github-aws-sdk-dotnet-automation@amazon.com" + git config --global user.name "aws-sdk-dotnet-automation" + # Retrieve the tag name to be deleted + - name: Read Tag Name + id: read-tag-name + run: | + tag=$(autover changelog --tag-name) + echo "TAG=$tag" >> $GITHUB_OUTPUT + # Delete the tag created by AutoVer and the release branch + - name: Clean up + run: | + git fetch origin + git push --delete origin ${{ steps.read-tag-name.outputs.TAG }} + git push origin --delete releases/next-release \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..50e0ba1 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,117 @@ +## Release 2024-10-15 + +### Amazon.Extensions.Configuration.SystemsManager (6.2.2) +* Update System.Text.Json for .NET Standard 2.0 target to version 8.0.5. + +## Release 2024-07-30 + +### Amazon.Extensions.Configuration.SystemsManager (6.2.1) +* For .NET Standard 2.0 target updated the dependency version of System.Text.Json to version 8.0.4 + +## Release 2024-06-19 + +### Amazon.Extensions.Configuration.SystemsManager (6.2.0) +* **Breaking Change:** Throw exception if duplicate Systems Manager parameter keys (case insensitive) are detected irrespective of whether the parameter is optional or not. + +## Release 2024-04-20 + +### Amazon.Extensions.Configuration.SystemsManager (6.1.1) +* Update User-Agent string + +## Release 2024-03-29 + +### Amazon.Extensions.Configuration.SystemsManager (6.1.0) +* Pull request [#163](https://github.com/aws/aws-dotnet-extensions-configuration/pull/163) adding .NET 8 target. Thanks [Jon Armen](https://github.com/jon-armen) + +## Release 2023-09-21 + +### Amazon.Extensions.Configuration.SystemsManager (6.0.0) +* BREAKING CHANGE: Added StringList handling in default parameter processor. +* Note: This is re-releasing 5.1.1 as a major version bump because it introduced a change in behavior if a user was already handling StringList parameters. + +## Release 2023-09-11 + +### Amazon.Extensions.Configuration.SystemsManager (5.1.1) +* Pull request [#142](https://github.com/aws/aws-dotnet-extensions-configuration/pull/142) Added StringList handling in default parameter processor. Thanks [ArtemMelnychenko](https://github.com/Plazmius) +* Note: We have unlisted and re-released this as 6.0.0. due to the breaking change reported in [#156](https://github.com/aws/aws-dotnet-extensions-configuration/issues/156) + +## Release 2023-05-31 + +### Amazon.Extensions.Configuration.SystemsManager (5.1.0) +* Pull request [#150](https://github.com/aws/aws-dotnet-extensions-configuration/pull/150) Adding .NET Standard 2.0 support for .NET Framework applications. Thanks [Matthew Heaton](https://github.com/heatonmatthew) + +## Release 2023-03-21 + +### Amazon.Extensions.Configuration.SystemsManager (5.0.2) +* Fixed an issue where AppConfig JSON configuration with charset information in ContentType was not being parsed. + +## Release 2023-03-06 + +### Amazon.Extensions.Configuration.SystemsManager (5.0.1) +* Fixed an issue where JsonParameterProcessor was prefixing `:` character to parameter properties while retrieving parameter by name. + +## Release 2023-02-07 + +### Amazon.Extensions.Configuration.SystemsManager (5.0.0) +* **Breaking Change:** Fixed issue when parsing JSON SSM parameter values to include the relative SSM parameter name to the JSON property names. + +## Release 2023-02-01 + +### Amazon.Extensions.Configuration.SystemsManager (4.0.1) +* Merged PR [#128](https://github.com/aws/aws-dotnet-extensions-configuration/pull/128) fixed issue parsing AppConfig response when services returns empty response for no changes. Thanks [Tyler Ohlsen](https://github.com/tylerohlsen) + +## Release 2022-06-28 + +### Amazon.Extensions.Configuration.SystemsManager (4.0.0) +* Merged PR [#99](https://github.com/aws/aws-dotnet-extensions-configuration/pull/99) adding support for using AppConfig Lambda extension. Thanks [mgorski-mg](https://github.com/mgorski-mg) +* Merged PR [#69](https://github.com/aws/aws-dotnet-extensions-configuration/pull/69) making IParameterProcessor used for customizing data from AWS into config values more genenric. Thanks [warej](https://github.com/warej) +* Merged PR [#59](https://github.com/aws/aws-dotnet-extensions-configuration/pull/59) removing dependency on Newtonsoft.JSON and now use System.Text.Json. Thanks [Adilson de Almeida Junior](https://github.com/Adilson) +* Accessing AppConfig data now uses the AWS AppConfig Data APIs StargConfigurationSession and GetLatestConfiguration +* **Breaking Change:** The IncludeParameter, GetKey and GetValue methods were removed from IParameterProcessor in favor of the new generic ProcessParameters method allowing more flexibility in implementations. +* **Breaking Change:** `ClientId` was removed from `AppConfigConfigurationSource`. +* **Breaking Change:** When using AppConfig IAM permissions for `appconfig:StartConfigurationSession` and `appconfig:GetLatestConfiguration` are now required and `appconfig:GetConfiguration` is not longer required. + +## Release 2021-08-18 + +### Amazon.Extensions.Configuration.SystemsManager (3.0.0) +* Merged PR [#82](https://github.com/aws/aws-dotnet-extensions-configuration/pull/82) Adding [AWS AppConfig](https://docs.aws.amazon.com/appconfig/latest/userguide/what-is-appconfig.html) support. Thanks [Michał Górski](https://github.com/mgorski-mg) + +## Release 2021-07-23 + +### Amazon.Extensions.Configuration.SystemsManager (2.1.1) +* Update AWSSDK.Extensions.NETCore.Setup and AWSSDK.SimpleSystemsManagement package references for SSO credential support. + +## Release 2021-03-30 + +### Amazon.Extensions.Configuration.SystemsManager (2.1.0) +* Update AWS SDK dependencies to version 3.7 + +## Release 2020-10-09 + +### Amazon.Extensions.Configuration.SystemsManager (2.0.0) +* Merged PR [#75](https://github.com/aws/aws-dotnet-extensions-configuration/pull/75) Update AWS SDK dependencies to 3.5. Thanks [Doug Ferris](https://github.com/doug-ferris) +* Update Newtonsoft.Json to version 12. + +## Release 2019-07-23 + +### Amazon.Extensions.Configuration.SystemsManager (1.2.0) +* Merged PR [#41](https://github.com/aws/aws-dotnet-extensions-configuration/pull/41) Prevents stripping the first char when the Path is "/". Thanks [Ken Hundley](https://github.com/KenHundley) +* Merged PR [#44](https://github.com/aws/aws-dotnet-extensions-configuration/pull/44) Added `Filters` property to SystemsManagerConfigurationSource. Thanks [Zahy Hwary](https://github.com/zahycs) + +## Release 2019-04-09 + +### Amazon.Extensions.Configuration.SystemsManager (1.1.1) +* Added AWS Secrets Manager support. Thanks [Ken Hundley](https://github.com/KenHundley) +* Only trigger OnReload when values have changed. +* Update version of the AWS SDK for .NET to 3.3.100 + +## Release 2019-01-11 + +### Amazon.Extensions.Configuration.SystemsManager (1.0.1) +* Made Analysers for development/local only + + +## Release 2019-12-17 + +### Amazon.Extensions.Configuration.SystemsManager (1.0.0) +* Initial release diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7c877bd..79803e3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -39,6 +39,49 @@ To send us a pull request, please: GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). +## Adding a `change file` to your contribution branch + +Each contribution branch should include a `change file` that contains a changelog message for each project that has been updated, as well as the type of increment to perform for those changes when versioning the project. + +A `change file` looks like the following example: +```json +{ + "Projects": [ + { + "Name": "Amazon.Extensions.Configuration.SystemsManager", + "Type": "Patch", + "ChangelogMessages": [ + "Fixed an issue causing a failure somewhere" + ] + } + ] +} +``` +The `change file` lists all the modified projects, the changelog message for each project as well as the increment type. + +These files are located in the repo at .autover/changes/ + +You can use the `AutoVer` tool to create the change file. You can install it using the following command: +``` +dotnet tool install -g AutoVer +``` + +You can create the `change file` using the following command: +``` +autover change --project-name "Amazon.Extensions.Configuration.SystemsManager" -m "Fixed an issue causing a failure somewhere +``` +Note: Make sure to run the command from the root of the repository. + +You can update the command to specify which project you are updating. +The available projects are: +* Amazon.Extensions.Configuration.SystemsManager + +The possible increment types are: +* Patch +* Minor +* Major + +Note: You do not need to create a new `change file` for every changelog message or project within your branch. You can create one `change file` that contains all the modified projects and the changelog messages. ## Finding contributions to work on Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/aws/aws-dotnet-extensions-configuration/labels/help%20wanted) issues is a great place to start. diff --git a/README.md b/README.md index e9dc3b2..08e334f 100644 --- a/README.md +++ b/README.md @@ -144,6 +144,35 @@ public class SampleLambda } ``` +# Hierarchical configuration data +Let's assume we want to load configuration per the below class hierarchy: +```csharp +public class DemoConfig +{ + public string TestItem { get; set; } + public DemoSubConfig SubConfig { get; set; } +} + +public class DemoSubConfig +{ + public string SubItem { get; set; } +} +``` +In System Manager parameter store, these hierarchical values could be represented with below names (notice the use of `/` delimiter): +| Name | Type | +| :-------- | :------- | +| /my-application/Config/TestItem | String | +| /my-application/Config/SubConfig/SubItem | String | + +Using `WebApplicationBuilder` as an example, the above configuration hierarchy could be loaded using below code: +```csharp +var builder = WebApplication.CreateBuilder(args); + +builder.Configuration.AddSystemsManager($"/my-application/"); + +builder.Services.Configure(builder.Configuration.GetSection("Config")); +``` + ## Samples ### Custom ParameterProcessor Sample @@ -209,7 +238,7 @@ For more information and other configurable options please refer to [Configuring # Permissions ## Parameter Store -The AWS credentials used must have access to the `ssm:GetParameters` service operation from AWS System Manager. Below is an example IAM policy for this action. +The AWS credentials used must have access to the `ssm:GetParametersByPath` service operation from AWS System Manager. Below is an example IAM policy for this action. ```JSON { "Version": "2012-10-17", @@ -217,7 +246,7 @@ The AWS credentials used must have access to the `ssm:GetParameters` service ope { "Sid": "SSMPermissionStatement", "Effect": "Allow", - "Action": "ssm:GetParameters", + "Action": "ssm:GetParametersByPath", "Resource": "arn:aws:ssm:${Region}:${Account}:parameter/${ParameterNamePrefix}*" } ] diff --git a/RELEASE.CHANGELOG.md b/RELEASE.CHANGELOG.md deleted file mode 100644 index 696dd39..0000000 --- a/RELEASE.CHANGELOG.md +++ /dev/null @@ -1,78 +0,0 @@ -### Release 2023-09-21 -* **Amazon.Extensions.Configuration.SystemsManager (6.0.0)** - * BREAKING CHANGE: Added StringList handling in default parameter processor. - * Note: This is re-releasing 5.1.1 as a major version bump because it introduced a change in behavior if a user was already handling StringList parameters. - -### Release 2023-09-11 -* **Amazon.Extensions.Configuration.SystemsManager (5.1.1)** - * Pull request [#142](https://github.com/aws/aws-dotnet-extensions-configuration/pull/142) Added StringList handling in default parameter processor. Thanks [ArtemMelnychenko](https://github.com/Plazmius) - * Note: We have unlisted and re-released this as 6.0.0. due to the breaking change reported in [#156](https://github.com/aws/aws-dotnet-extensions-configuration/issues/156) - -### Release 2023-05-31 -* **Amazon.Extensions.Configuration.SystemsManager (5.1.0)** - * Pull request [#150](https://github.com/aws/aws-dotnet-extensions-configuration/pull/150) Adding .NET Standard 2.0 support for .NET Framework applications. Thanks [Matthew Heaton](https://github.com/heatonmatthew) - -### Release 2023-03-21 -* **Amazon.Extensions.Configuration.SystemsManager (5.0.2)** - * Fixed an issue where AppConfig JSON configuration with charset information in ContentType was not being parsed. - -### Release 2023-03-06 -* **Amazon.Extensions.Configuration.SystemsManager (5.0.1)** - * Fixed an issue where JsonParameterProcessor was prefixing `:` character to parameter properties while retrieving parameter by name. - -### Release 2023-02-07 -* **Amazon.Extensions.Configuration.SystemsManager (5.0.0)** - * **Breaking Change:** Fixed issue when parsing JSON SSM parameter values to include the relative SSM parameter name to the JSON property names. - -### Release 2023-02-01 -* **Amazon.Extensions.Configuration.SystemsManager (4.0.1)** - * Merged PR [#128](https://github.com/aws/aws-dotnet-extensions-configuration/pull/128) fixed issue parsing AppConfig response when services returns empty response for no changes. Thanks [Tyler Ohlsen](https://github.com/tylerohlsen) - -### Release 2022-06-28 -* **Amazon.Extensions.Configuration.SystemsManager (4.0.0)** - * Merged PR [#99](https://github.com/aws/aws-dotnet-extensions-configuration/pull/99) adding support for using AppConfig Lambda extension. Thanks [mgorski-mg](https://github.com/mgorski-mg) - * Merged PR [#69](https://github.com/aws/aws-dotnet-extensions-configuration/pull/69) making IParameterProcessor used for customizing data from AWS into config values more genenric. Thanks [warej](https://github.com/warej) - * Merged PR [#59](https://github.com/aws/aws-dotnet-extensions-configuration/pull/59) removing dependency on Newtonsoft.JSON and now use System.Text.Json. Thanks [Adilson de Almeida Junior](https://github.com/Adilson) - * Accessing AppConfig data now uses the AWS AppConfig Data APIs StargConfigurationSession and GetLatestConfiguration - * **Breaking Change:** The IncludeParameter, GetKey and GetValue methods were removed from IParameterProcessor in favor of the new generic ProcessParameters method allowing more flexibility in implementations. - * **Breaking Change:** `ClientId` was removed from `AppConfigConfigurationSource`. - * **Breaking Change:** When using AppConfig IAM permissions for `appconfig:StartConfigurationSession` and `appconfig:GetLatestConfiguration` are now required and `appconfig:GetConfiguration` is not longer required. - - - -### Release 2021-08-18 -* **Amazon.Extensions.Configuration.SystemsManager (3.0.0)** - * Merged PR [#82](https://github.com/aws/aws-dotnet-extensions-configuration/pull/82) Adding [AWS AppConfig](https://docs.aws.amazon.com/appconfig/latest/userguide/what-is-appconfig.html) support. Thanks [Michał Górski](https://github.com/mgorski-mg) - -### Release 2021-07-23 -* **Amazon.Extensions.Configuration.SystemsManager (2.1.1)** - * Update AWSSDK.Extensions.NETCore.Setup and AWSSDK.SimpleSystemsManagement package references for SSO credential support. - -### Release 2021-03-30 -* **Amazon.Extensions.Configuration.SystemsManager (2.1.0)** - * Update AWS SDK dependencies to version 3.7 - -### Release 2020-10-09 -* **Amazon.Extensions.Configuration.SystemsManager (2.0.0)** - * Merged PR [#75](https://github.com/aws/aws-dotnet-extensions-configuration/pull/75) Update AWS SDK dependencies to 3.5. Thanks [Doug Ferris](https://github.com/doug-ferris) - * Update Newtonsoft.Json to version 12. - -### Release 2019-07-23 -* **Amazon.Extensions.Configuration.SystemsManager (1.2.0)** - * Merged PR [#41](https://github.com/aws/aws-dotnet-extensions-configuration/pull/41) Prevents stripping the first char when the Path is "/". Thanks [Ken Hundley](https://github.com/KenHundley) - * Merged PR [#44](https://github.com/aws/aws-dotnet-extensions-configuration/pull/44) Added `Filters` property to SystemsManagerConfigurationSource. Thanks [Zahy Hwary](https://github.com/zahycs) - -### Release 2019-04-09 -* **Amazon.Extensions.Configuration.SystemsManager (1.1.1)** - * Added AWS Secrets Manager support. Thanks [Ken Hundley](https://github.com/KenHundley) - * Only trigger OnReload when values have changed. - * Update version of the AWS SDK for .NET to 3.3.100 - -### Release 2019-01-11 -* **Amazon.Extensions.Configuration.SystemsManager (1.0.1)** - * Made Analysers for development/local only - - -### Release 2019-12-17 -* **Amazon.Extensions.Configuration.SystemsManager (1.0.0)** - * Initial release diff --git a/buildtools/ci.buildspec.yml b/buildtools/ci.buildspec.yml new file mode 100644 index 0000000..c6ce1b5 --- /dev/null +++ b/buildtools/ci.buildspec.yml @@ -0,0 +1,16 @@ +version: 0.2 + +phases: + install: + runtime-versions: + dotnet: 8.x + build: + commands: + - dotnet test test/Amazon.Extensions.Configuration.SystemsManager.Tests/Amazon.Extensions.Configuration.SystemsManager.Tests.csproj -c Release --logger trx --results-directory ./testresults + - dotnet test test/Amazon.Extensions.Configuration.SystemsManager.Integ/Amazon.Extensions.Configuration.SystemsManager.Integ.csproj -c Release --logger trx --results-directory ./testresults +reports: + aws-ssm-data-protection-provider-for-aspnet-tests: + file-format: VisualStudioTrx + files: + - '**/*' + base-directory: './testresults' \ No newline at end of file diff --git a/public.snk b/public.snk index be0ad9c..709adb3 100644 Binary files a/public.snk and b/public.snk differ diff --git a/samples/Samples/Samples.csproj b/samples/Samples/Samples.csproj index 8873b50..f5be131 100644 --- a/samples/Samples/Samples.csproj +++ b/samples/Samples/Samples.csproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 enable enable diff --git a/src/Amazon.Extensions.Configuration.SystemsManager/Amazon.Extensions.Configuration.SystemsManager.csproj b/src/Amazon.Extensions.Configuration.SystemsManager/Amazon.Extensions.Configuration.SystemsManager.csproj index 19e0e31..d1d55c6 100644 --- a/src/Amazon.Extensions.Configuration.SystemsManager/Amazon.Extensions.Configuration.SystemsManager.csproj +++ b/src/Amazon.Extensions.Configuration.SystemsManager/Amazon.Extensions.Configuration.SystemsManager.csproj @@ -2,25 +2,25 @@ - netstandard2.0;netcoreapp3.1;net6.0 + netstandard2.0;netcoreapp3.1;net6.0;net8.0 Amazon.Extensions.Configuration.SystemsManager Amazon.Extensions.Configuration.SystemsManager Library - 6.0.0 + 6.2.2 true Amazon.Extensions.Configuration.SystemsManager .NET Configuration Extensions for AWS Systems Manager Amazon.Extensions.Configuration.SystemsManager .NET Configuration Extensions for AWS Systems Manager Amazon Web Services - 2018-2023 + 2018-2024 AWS;Amazon;aws-sdk-v3;SimpleSystemsManagement;configuration https://github.com/aws/aws-dotnet-extensions-configuration/ LICENSE icon.png https://github.com/aws/aws-dotnet-extensions-configuration/ Amazon Web Services - + true ../ruleset.xml @@ -29,27 +29,17 @@ - - - - true - ..\..\public.snk - - - - - true - $(AWSKeyFile) - - - + + true + ..\..\public.snk + - - - + + + - + diff --git a/src/Amazon.Extensions.Configuration.SystemsManager/AppConfig/AppConfigProcessor.cs b/src/Amazon.Extensions.Configuration.SystemsManager/AppConfig/AppConfigProcessor.cs index 36777dd..e3acf2c 100644 --- a/src/Amazon.Extensions.Configuration.SystemsManager/AppConfig/AppConfigProcessor.cs +++ b/src/Amazon.Extensions.Configuration.SystemsManager/AppConfig/AppConfigProcessor.cs @@ -25,7 +25,11 @@ namespace Amazon.Extensions.Configuration.SystemsManager.AppConfig { + // Types that own disposable fields should be disposable. This warning is okay to ignore because + // the instance of IAmazonAppConfigData is meant to last for the length of the application. +#pragma warning disable CA1001 public class AppConfigProcessor : ISystemsManagerProcessor +#pragma warning restore CA1001 { private AppConfigConfigurationSource Source { get; } private IDictionary LastConfig { get; set; } diff --git a/src/Amazon.Extensions.Configuration.SystemsManager/DefaultParameterProcessor.cs b/src/Amazon.Extensions.Configuration.SystemsManager/DefaultParameterProcessor.cs index 8076511..a5d637b 100644 --- a/src/Amazon.Extensions.Configuration.SystemsManager/DefaultParameterProcessor.cs +++ b/src/Amazon.Extensions.Configuration.SystemsManager/DefaultParameterProcessor.cs @@ -38,7 +38,11 @@ public virtual string GetKey(Parameter parameter, string path) var name = parameter.Name.StartsWith(path, StringComparison.OrdinalIgnoreCase) ? parameter.Name.Substring(path.Length) : parameter.Name; +#if NETCOREAPP3_1_OR_GREATER + return name.TrimStart('/').Replace("/", KeyDelimiter, StringComparison.InvariantCulture); +#else return name.TrimStart('/').Replace("/", KeyDelimiter); +#endif } public virtual string GetValue(Parameter parameter, string path) => parameter.Value; @@ -60,11 +64,29 @@ public virtual IDictionary ProcessParameters(IEnumerable p.Key); + var duplicateKeys = result.Where(r => stringListKeys.Contains(r.Key, StringComparer.OrdinalIgnoreCase)).Select(r => r.Key); + if (duplicateKeys.Count() > 0) + { + throw new DuplicateParameterException($"Duplicate parameters '{string.Join(";", duplicateKeys)}' found. Parameter keys are case-insensitive."); + } + + result.AddRange(parameterList); } else { - result.Add(new KeyValuePair(GetKey(parameter, path), GetValue(parameter, path))); + string parameterKey = GetKey(parameter, path); + + // Check for duplicate parameter key. + if (result.Any(r => string.Equals(r.Key, parameterKey, StringComparison.OrdinalIgnoreCase))) + { + throw new DuplicateParameterException($"Duplicate parameter '{parameterKey}' found. Parameter keys are case-insensitive."); + } + + result.Add(new KeyValuePair(parameterKey, GetValue(parameter, path))); } } diff --git a/src/Amazon.Extensions.Configuration.SystemsManager/DuplicateParameterException.cs b/src/Amazon.Extensions.Configuration.SystemsManager/DuplicateParameterException.cs new file mode 100644 index 0000000..584611f --- /dev/null +++ b/src/Amazon.Extensions.Configuration.SystemsManager/DuplicateParameterException.cs @@ -0,0 +1,55 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System; + +namespace Amazon.Extensions.Configuration.SystemsManager +{ + /// + /// This exception is thrown when duplicate Systems Manager parameter keys (case insensitive) are + /// detected irrespective of whether the parameter is optional or not. + /// + /// For example, keys /some-path/some-key and /some-path/SOME-KEY are considered as duplicates. + /// + /// + public class DuplicateParameterException : ArgumentException + { + /// + /// Initializes a new instance of DuplicateParameterException. + /// + public DuplicateParameterException() + { + } + + /// + /// Initializes a new instance of DuplicateParameterException. + /// + /// The message that describes the error. + public DuplicateParameterException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of DuplicateParameterException. + /// + /// The message that describes the error. + /// The exception that is the cause of the current exception, or a null reference if no inner exception is specified. + public DuplicateParameterException(string message, Exception innerException) + : base(message, innerException) + { + } + } +} diff --git a/src/Amazon.Extensions.Configuration.SystemsManager/ISystemsManagerConfigurationSource.cs b/src/Amazon.Extensions.Configuration.SystemsManager/ISystemsManagerConfigurationSource.cs index 4d6b7ba..89b84b3 100644 --- a/src/Amazon.Extensions.Configuration.SystemsManager/ISystemsManagerConfigurationSource.cs +++ b/src/Amazon.Extensions.Configuration.SystemsManager/ISystemsManagerConfigurationSource.cs @@ -27,7 +27,9 @@ public interface ISystemsManagerConfigurationSource : IConfigurationSource /// /// Determines if loading configuration data from AWS Systems Manager Parameter Store is optional. /// +#pragma warning disable CA1716 // Identifiers should not match keywords: This would be a breaking change to change this name now. bool Optional { get; set; } +#pragma warning restore CA1716 // Identifiers should not match keywords /// /// Parameters will be reloaded from the AWS Systems Manager Parameter Store after the specified time frame diff --git a/src/Amazon.Extensions.Configuration.SystemsManager/Internal/JsonConfigurationParser.cs b/src/Amazon.Extensions.Configuration.SystemsManager/Internal/JsonConfigurationParser.cs index b6ed63a..4492a58 100644 --- a/src/Amazon.Extensions.Configuration.SystemsManager/Internal/JsonConfigurationParser.cs +++ b/src/Amazon.Extensions.Configuration.SystemsManager/Internal/JsonConfigurationParser.cs @@ -76,7 +76,7 @@ private void VisitElement(JsonElement element) VisitPrimitive(element); break; case JsonValueKind.Null: - VisitNull(element); + VisitNull(); break; } @@ -95,7 +95,7 @@ private void VisitArray(JsonElement array) } } - private void VisitNull(JsonElement data) + private void VisitNull() { var key = _currentPath; _data[key] = null; diff --git a/src/Amazon.Extensions.Configuration.SystemsManager/Internal/ServiceClientAppender.cs b/src/Amazon.Extensions.Configuration.SystemsManager/Internal/ServiceClientAppender.cs index 68b94c1..c612b70 100644 --- a/src/Amazon.Extensions.Configuration.SystemsManager/Internal/ServiceClientAppender.cs +++ b/src/Amazon.Extensions.Configuration.SystemsManager/Internal/ServiceClientAppender.cs @@ -23,14 +23,21 @@ public static class ServiceClientAppender { private const string UserAgentHeader = "User-Agent"; private static readonly string AssemblyVersion = typeof(AppConfigProcessor).GetTypeInfo().Assembly.GetName().Version.ToString(); + private static readonly string UserAgentSuffix = $"lib/SSMConfigProvider#{AssemblyVersion}"; public static void ServiceClientBeforeRequestEvent(object sender, RequestEventArgs e) { if (e is WebServiceRequestEventArgs args) { - if (args.Headers.ContainsKey(UserAgentHeader)) + if (args.Headers.ContainsKey(UserAgentHeader) && +#if NETCOREAPP3_1_OR_GREATER + !args.Headers[UserAgentHeader].Contains(UserAgentSuffix, System.StringComparison.InvariantCulture) +#else + !args.Headers[UserAgentHeader].Contains(UserAgentSuffix) +#endif + ) { - args.Headers[UserAgentHeader] = args.Headers[UserAgentHeader] + " SSMConfigProvider/" + AssemblyVersion; + args.Headers[UserAgentHeader] = args.Headers[UserAgentHeader] + " " + UserAgentSuffix; } } } diff --git a/src/Amazon.Extensions.Configuration.SystemsManager/JsonParameterProcessor.cs b/src/Amazon.Extensions.Configuration.SystemsManager/JsonParameterProcessor.cs index 9f4bd15..498053f 100644 --- a/src/Amazon.Extensions.Configuration.SystemsManager/JsonParameterProcessor.cs +++ b/src/Amazon.Extensions.Configuration.SystemsManager/JsonParameterProcessor.cs @@ -40,6 +40,13 @@ public override IDictionary ProcessParameters(IEnumerable - netcoreapp3.1 - + net6.0;net8.0 + true false - - - - - + + + + + all runtime; build; native; contentfiles; analyzers - diff --git a/test/Amazon.Extensions.Configuration.SystemsManager.Tests/Amazon.Extensions.Configuration.SystemsManager.Tests.csproj b/test/Amazon.Extensions.Configuration.SystemsManager.Tests/Amazon.Extensions.Configuration.SystemsManager.Tests.csproj index d3ed313..ef406cf 100644 --- a/test/Amazon.Extensions.Configuration.SystemsManager.Tests/Amazon.Extensions.Configuration.SystemsManager.Tests.csproj +++ b/test/Amazon.Extensions.Configuration.SystemsManager.Tests/Amazon.Extensions.Configuration.SystemsManager.Tests.csproj @@ -1,8 +1,8 @@  - netcoreapp3.1 - + net6.0;net8.0 + true false @@ -13,14 +13,13 @@ - - - - + + + + all runtime; build; native; contentfiles; analyzers - diff --git a/test/Amazon.Extensions.Configuration.SystemsManager.Tests/DefaultParameterProcessorTests.cs b/test/Amazon.Extensions.Configuration.SystemsManager.Tests/DefaultParameterProcessorTests.cs index 5f7a705..ef44742 100644 --- a/test/Amazon.Extensions.Configuration.SystemsManager.Tests/DefaultParameterProcessorTests.cs +++ b/test/Amazon.Extensions.Configuration.SystemsManager.Tests/DefaultParameterProcessorTests.cs @@ -71,5 +71,31 @@ public void ProcessParametersRootTest() Assert.All(data, item => Assert.Equal(item.Value, item.Key)); } + + [Fact] + public void DuplicateSimpleParametersTest() + { + var parameters = new List + { + new Parameter {Name = "/start/path/p1", Value = "p1:1"}, + new Parameter {Name = "/start/path/P1", Value = "p1:2"} + }; + + const string path = "/start/path"; + Assert.Throws(() => _parameterProcessor.ProcessParameters(parameters, path)); + } + + [Fact] + public void DuplicateStringListParametersTest() + { + var parameters = new List + { + new Parameter {Name = "/string-list/multiple", Value = "p1,p2,p3", Type = ParameterType.StringList}, + new Parameter {Name = "/string-list/MULTIPLE", Value = "p3,p5,p6", Type = ParameterType.StringList} + }; + + const string path = "/string-list"; + Assert.Throws(() => _parameterProcessor.ProcessParameters(parameters, path)); + } } } \ No newline at end of file diff --git a/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonParameterProcessorTests.cs b/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonParameterProcessorTests.cs index 04a578b..a4acec1 100644 --- a/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonParameterProcessorTests.cs +++ b/test/Amazon.Extensions.Configuration.SystemsManager.Tests/JsonParameterProcessorTests.cs @@ -44,5 +44,19 @@ public void ProcessParametersTest() Assert.All(expected, item => Assert.Equal(item.Value, data[item.Key])); } + + [Fact] + public void DuplicateParametersTest() + { + var parameters = new List + { + new Parameter {Name = "/p1", Value = "{\"p1\": \"p1\"}"}, + new Parameter {Name = "p2", Value = "{\"p2\": \"p2\"}"}, + new Parameter {Name = "p1", Value = "{\"P1\": \"p1-1\"}"}, + }; + + const string path = "/"; + Assert.Throws(() => _parameterProcessor.ProcessParameters(parameters, path)); + } } } \ No newline at end of file