diff --git a/build.proj b/build.proj
index d5cdcf73ef..8d21a86c63 100644
--- a/build.proj
+++ b/build.proj
@@ -515,7 +515,7 @@
-
+
diff --git a/eng/pipelines/common/templates/jobs/build-signed-package-job.yml b/eng/pipelines/common/templates/jobs/build-signed-package-job.yml
index 939ad9ab0d..8a90a42953 100644
--- a/eng/pipelines/common/templates/jobs/build-signed-package-job.yml
+++ b/eng/pipelines/common/templates/jobs/build-signed-package-job.yml
@@ -73,7 +73,7 @@ jobs:
# These variables are sourced from common-variables.yml.
abstractionsAssemblyFileVersion: $(abstractionsAssemblyFileVersion)
abstractionsPackageVersion: $(abstractionsPackageVersion)
- configuration: $(Configuration)
+ buildConfiguration: $(Configuration)
mdsAssemblyFileVersion: $(mdsAssemblyFileVersion)
mdsPackageVersion: $(mdsPackageVersion)
referenceType: Package
diff --git a/eng/pipelines/common/templates/steps/ensure-dotnet-version.yml b/eng/pipelines/common/templates/steps/ensure-dotnet-version.yml
index fd6ba9674b..91724bf566 100644
--- a/eng/pipelines/common/templates/steps/ensure-dotnet-version.yml
+++ b/eng/pipelines/common/templates/steps/ensure-dotnet-version.yml
@@ -10,6 +10,13 @@
# Reason for not using UseDotNet task:
# [BUG]: UseDotNet task installs x86 build on Windows arm64
# https://github.com/microsoft/azure-pipelines-tasks/issues/20300
+#
+# A possible workaround is discussed here:
+#
+# https://github.com/microsoft/azure-pipelines-tasks/issues/16501
+#
+# TODO: See if we can eliminate this template and just use the above workaround
+# for the Windows x86 builds.
parameters:
- # Directory where dotnet binaries should be installed. If not specified, defaults to the
diff --git a/eng/pipelines/common/templates/steps/generate-nuget-package-step.yml b/eng/pipelines/common/templates/steps/generate-nuget-package-step.yml
index 3a57aa93a8..7ed33cbdc2 100644
--- a/eng/pipelines/common/templates/steps/generate-nuget-package-step.yml
+++ b/eng/pipelines/common/templates/steps/generate-nuget-package-step.yml
@@ -61,6 +61,6 @@ steps:
inputs:
command: custom
${{ if parameters.generateSymbolsPackage }}:
- arguments: 'pack -Symbols -SymbolPackageFormat snupkg ${{parameters.nuspecPath}} -Version ${{parameters.NugetPackageVersion}} -OutputDirectory ${{parameters.OutputDirectory}} -properties "COMMITID=$(CommitHead);Configuration=${{parameters.buildConfiguration}};ReferenceType=${{parameters.referenceType}};${{parameters.properties}}"'
+ arguments: 'pack -Symbols -SymbolPackageFormat snupkg ${{parameters.nuspecPath}} -Version ${{parameters.packageVersion}} -OutputDirectory ${{parameters.outputDirectory}} -properties "COMMITID=$(CommitHead);Configuration=${{parameters.buildConfiguration}};ReferenceType=${{parameters.referenceType}};${{parameters.properties}}"'
${{else }}:
- arguments: 'pack ${{parameters.nuspecPath}} -Version ${{parameters.NugetPackageVersion}} -OutputDirectory ${{parameters.OutputDirectory}} -properties "COMMITID=$(CommitHead);Configuration=${{parameters.buildConfiguration}};ReferenceType=${{parameters.referenceType}};${{parameters.properties}}"'
+ arguments: 'pack ${{parameters.nuspecPath}} -Version ${{parameters.packageVersion}} -OutputDirectory ${{parameters.outputDirectory}} -properties "COMMITID=$(CommitHead);Configuration=${{parameters.buildConfiguration}};ReferenceType=${{parameters.referenceType}};${{parameters.properties}}"'
diff --git a/eng/pipelines/common/templates/steps/update-config-file-step.yml b/eng/pipelines/common/templates/steps/update-config-file-step.yml
index f49e552323..2bb7426be2 100644
--- a/eng/pipelines/common/templates/steps/update-config-file-step.yml
+++ b/eng/pipelines/common/templates/steps/update-config-file-step.yml
@@ -124,6 +124,10 @@ parameters:
type: boolean
default: true
+ - name: WorkloadIdentityFederationServiceConnectionId
+ type: string
+ default: ''
+
steps:
# All properties should be added here, and this template should be used for any manipulation of the config.json file.
- pwsh: |
@@ -180,6 +184,7 @@ steps:
$p.IsDNSCachingSupportedCR=[System.Convert]::ToBoolean("${{parameters.IsDNSCachingSupportedCR }}")
$p.TracingEnabled=[System.Convert]::ToBoolean("${{parameters.TracingEnabled }}")
$p.EnclaveEnabled=[System.Convert]::ToBoolean("${{parameters.EnclaveEnabled }}")
+ $p.WorkloadIdentityFederationServiceConnectionId="${{parameters.WorkloadIdentityFederationServiceConnectionId }}"
}
$jdata | ConvertTo-Json | Set-Content "config.json"
workingDirectory: src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities
@@ -196,4 +201,4 @@ steps:
}
}
workingDirectory: src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities
- displayName: 'Read config.json [debug]'
+ displayName: '[Debug] Read config.json'
diff --git a/eng/pipelines/dotnet-sqlclient-ci-core.yml b/eng/pipelines/dotnet-sqlclient-ci-core.yml
index 9885684a12..9bf4fe85f2 100644
--- a/eng/pipelines/dotnet-sqlclient-ci-core.yml
+++ b/eng/pipelines/dotnet-sqlclient-ci-core.yml
@@ -93,6 +93,17 @@ parameters:
type: boolean
default: true
+- name: dotnetVerbosity
+ displayName: dotnet CLI Verbosity
+ type: string
+ default: normal
+ values:
+ - quiet
+ - minimal
+ - normal
+ - detailed
+ - diagnostic
+
variables:
- template: libraries/ci-build-variables.yml@self
@@ -114,11 +125,11 @@ stages:
# under the given artifact name.
- template: stages/build-abstractions-package-ci-stage.yml@self
parameters:
- buildConfiguration: ${{ parameters.buildConfiguration }}
+ abstractionsArtifactName: $(abstractionsArtifactName)
abstractionsPackageVersion: $(abstractionsPackageVersion)
- artifactName: $(abstractionsArtifactName)
- ${{if eq(parameters.debug, 'true')}}:
- verbosity: diagnostic
+ buildConfiguration: ${{ parameters.buildConfiguration }}
+ debug: ${{ parameters.debug }}
+ dotnetVerbosity: ${{ parameters.dotnetVerbosity }}
# Build MDS and its NuGet packages.
- stage: build_mds_akv_packages_stage
@@ -157,25 +168,29 @@ stages:
azureArtifactName: $(azureArtifactName)
azurePackageVersion: $(azurePackageVersion)
buildConfiguration: ${{ parameters.buildConfiguration }}
+ debug: ${{ parameters.debug }}
+ mdsArtifactName: $(mdsArtifactName)
+ mdsPackageVersion: $(mdsPackageVersion)
# When building via package references, we must depend on the Abstractions
- # package.
+ # and MDS packages
${{ if eq(parameters.referenceType, 'Package') }}:
dependsOn:
- build_abstractions_package_stage
+ - build_mds_akv_packages_stage
referenceType: ${{ parameters.referenceType }}
- ${{if eq(parameters.debug, 'true')}}:
- verbosity: diagnostic
+ dotnetVerbosity: ${{ parameters.dotnetVerbosity }}
# Run the stress tests, if desired.
- ${{ if eq(parameters.enableStressTests, true) }}:
- template: stages/stress-tests-ci-stage.yml@self
parameters:
buildConfiguration: ${{ parameters.buildConfiguration }}
- dependsOn: [build_mds_akv_packages_stage]
+ dependsOn:
+ - build_mds_akv_packages_stage
+ - build_azure_package_stage
pipelineArtifactName: $(artifactName)
mdsPackageVersion: $(mdsPackageVersion)
- ${{ if eq(parameters.debug, 'true') }}:
- verbosity: 'detailed'
+ dotnetVerbosity: ${{ parameters.dotnetVerbosity }}
# Run the MDS and AKV tests.
- template: common/templates/stages/ci-run-tests-stage.yml@self
@@ -190,9 +205,6 @@ stages:
mdsArtifactName: $(mdsArtifactName)
mdsPackageVersion: $(mdsPackageVersion)
testJobTimeout: ${{ parameters.testJobTimeout }}
- ${{ if eq(parameters.buildType, 'Package') }}:
- dependsOn: build_nugets
-
# When testing MDS via packages, we must depend on the Abstractions,
# Azure, and MDS packages.
${{ if eq(parameters.referenceType, 'Package') }}:
diff --git a/eng/pipelines/dotnet-sqlclient-ci-package-reference-pipeline.yml b/eng/pipelines/dotnet-sqlclient-ci-package-reference-pipeline.yml
index f9fc8ff8f8..4ce3004d5d 100644
--- a/eng/pipelines/dotnet-sqlclient-ci-package-reference-pipeline.yml
+++ b/eng/pipelines/dotnet-sqlclient-ci-package-reference-pipeline.yml
@@ -149,6 +149,18 @@ parameters:
type: object
default: [false, true]
+ # Dotnet CLI verbosity level.
+ - name: dotnetVerbosity
+ displayName: dotnet CLI Verbosity
+ type: string
+ default: normal
+ values:
+ - quiet
+ - minimal
+ - normal
+ - detailed
+ - diagnostic
+
extends:
template: dotnet-sqlclient-ci-core.yml@self
parameters:
@@ -157,6 +169,7 @@ extends:
referenceType: Package
codeCovTargetFrameworks: ${{ parameters.codeCovTargetFrameworks }}
debug: ${{ parameters.debug }}
+ dotnetVerbosity: ${{ parameters.dotnetVerbosity }}
enableStressTests: ${{ parameters.enableStressTests }}
targetFrameworks: ${{ parameters.targetFrameworks }}
targetFrameworksLinux: ${{ parameters.targetFrameworksLinux }}
diff --git a/eng/pipelines/dotnet-sqlclient-ci-project-reference-pipeline.yml b/eng/pipelines/dotnet-sqlclient-ci-project-reference-pipeline.yml
index 71f0bbf718..d35cca867c 100644
--- a/eng/pipelines/dotnet-sqlclient-ci-project-reference-pipeline.yml
+++ b/eng/pipelines/dotnet-sqlclient-ci-project-reference-pipeline.yml
@@ -149,6 +149,18 @@ parameters:
type: object
default: [false, true]
+ # Dotnet CLI verbosity level.
+ - name: dotnetVerbosity
+ displayName: dotnet CLI Verbosity
+ type: string
+ default: normal
+ values:
+ - quiet
+ - minimal
+ - normal
+ - detailed
+ - diagnostic
+
extends:
template: dotnet-sqlclient-ci-core.yml@self
parameters:
@@ -157,6 +169,7 @@ extends:
referenceType: Project
codeCovTargetFrameworks: ${{ parameters.codeCovTargetFrameworks }}
debug: ${{ parameters.debug }}
+ dotnetVerbosity: ${{ parameters.dotnetVerbosity }}
enableStressTests: ${{ parameters.enableStressTests }}
targetFrameworks: ${{ parameters.targetFrameworks }}
targetFrameworksLinux: ${{ parameters.targetFrameworksLinux }}
diff --git a/eng/pipelines/jobs/pack-abstractions-package-ci-job.yml b/eng/pipelines/jobs/pack-abstractions-package-ci-job.yml
index 13bf3618fb..d08cc1f7a6 100644
--- a/eng/pipelines/jobs/pack-abstractions-package-ci-job.yml
+++ b/eng/pipelines/jobs/pack-abstractions-package-ci-job.yml
@@ -12,21 +12,26 @@
parameters:
- # The version to apply to the Abstractions NuGet package and its assemblies.
- - name: abstractionsPackageVersion
- type: string
-
# The name to apply to the published pipeline artifact.
- - name: artifactName
+ - name: abstractionsArtifactName
type: string
default: Abstractions.Artifact
+ # The version to apply to the Abstractions NuGet package and its assemblies.
+ - name: abstractionsPackageVersion
+ type: string
+
# The type of build to test (Release or Debug)
- name: buildConfiguration
type: string
values:
- Release
- Debug
+
+ # True to enable extra debug steps and logging.
+ - name: debug
+ type: boolean
+ default: false
# The list of upstream jobs to depend on.
- name: dependsOn
@@ -34,7 +39,7 @@ parameters:
default: []
# The verbosity level for the dotnet CLI commands.
- - name: verbosity
+ - name: dotnetVerbosity
type: string
default: normal
values:
@@ -69,13 +74,14 @@ jobs:
# dotnet CLI arguments common to all commands.
- name: commonArguments
value: >-
- --verbosity ${{ parameters.verbosity }}
+ --verbosity ${{ parameters.dotnetVerbosity }}
# dotnet CLI arguments for build/test/pack commands
- name: buildArguments
value: >-
$(commonArguments)
--configuration ${{ parameters.buildConfiguration }}
+ -p:ForceMdsAssemblyNameSuffix=true
-p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }}
# Explicitly unset the $PLATFORM environment variable that is set by the
@@ -102,6 +108,11 @@ jobs:
steps:
+ # Emit environment variables if debug is enabled.
+ - ${{ if eq(parameters.debug, true) }}:
+ - pwsh: 'Get-ChildItem Env: | Sort-Object Name'
+ displayName: '[Debug] Print Environment Variables'
+
# Install the .NET 9.0 SDK.
- task: UseDotNet@2
displayName: Install .NET 9.0 SDK
@@ -112,18 +123,18 @@ jobs:
# We use the 'custom' command because the DotNetCoreCLI@2 task doesn't
# support all of our argument combinations for the different build steps.
- # Restore the solution.
+ # Restore the project.
- task: DotNetCoreCLI@2
- displayName: Restore Solution
+ displayName: Restore Project
inputs:
command: custom
custom: restore
projects: $(project)
arguments: $(commonArguments)
- # Build the solution.
+ # Build the project.
- task: DotNetCoreCLI@2
- displayName: Build Solution
+ displayName: Build Project
inputs:
command: custom
custom: build
@@ -144,5 +155,5 @@ jobs:
displayName: Publish Pipeline Artifact
inputs:
targetPath: $(dotnetPackagesDir)
- artifactName: ${{ parameters.artifactName }}
+ artifactName: ${{ parameters.abstractionsArtifactName }}
publishLocation: pipeline
diff --git a/eng/pipelines/jobs/pack-azure-package-ci-job.yml b/eng/pipelines/jobs/pack-azure-package-ci-job.yml
index 9cc33eadb1..d0551c0960 100644
--- a/eng/pipelines/jobs/pack-azure-package-ci-job.yml
+++ b/eng/pipelines/jobs/pack-azure-package-ci-job.yml
@@ -40,12 +40,28 @@ parameters:
values:
- Release
- Debug
+
+ # True to enable extra debug steps and logging.
+ - name: debug
+ type: boolean
+ default: false
# The list of upstream jobs to depend on.
- name: dependsOn
type: object
default: []
+ # The verbosity level for the dotnet CLI commands.
+ - name: dotnetVerbosity
+ type: string
+ default: normal
+ values:
+ - quiet
+ - minimal
+ - normal
+ - detailed
+ - diagnostic
+
# The reference type to use:
#
# Project - dependent projects are referenced directly.
@@ -57,17 +73,6 @@ parameters:
- Package
- Project
- # The verbosity level for the dotnet CLI commands.
- - name: verbosity
- type: string
- default: normal
- values:
- - quiet
- - minimal
- - normal
- - detailed
- - diagnostic
-
jobs:
- job: pack_azure_package_job
@@ -93,8 +98,9 @@ jobs:
# dotnet CLI arguments common to all commands.
- name: commonArguments
value: >-
- --verbosity ${{ parameters.verbosity }}
+ --verbosity ${{ parameters.dotnetVerbosity }}
-p:ReferenceType=${{ parameters.referenceType }}
+ -p:ForceMdsAssemblyNameSuffix=true
-p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }}
# dotnet CLI arguments for build/test/pack commands
@@ -128,6 +134,11 @@ jobs:
steps:
+ # Emit environment variables if debug is enabled.
+ - ${{ if eq(parameters.debug, true) }}:
+ - pwsh: 'Get-ChildItem Env: | Sort-Object Name'
+ displayName: '[Debug] Print Environment Variables'
+
# We have a few extra steps for Package reference builds.
- ${{ if eq(parameters.referenceType, 'Package') }}:
@@ -152,18 +163,18 @@ jobs:
# We use the 'custom' command because the DotNetCoreCLI@2 task doesn't
# support all of our argument combinations for the different build steps.
- # Restore the solution.
+ # Restore the project.
- task: DotNetCoreCLI@2
- displayName: Restore Solution
+ displayName: Restore Project
inputs:
command: custom
custom: restore
projects: $(project)
arguments: $(commonArguments)
- # Build the solution.
+ # Build the project.
- task: DotNetCoreCLI@2
- displayName: Build Solution
+ displayName: Build Project
inputs:
command: custom
custom: build
diff --git a/eng/pipelines/jobs/test-abstractions-package-ci-job.yml b/eng/pipelines/jobs/test-abstractions-package-ci-job.yml
index a5e0f98978..e908f71562 100644
--- a/eng/pipelines/jobs/test-abstractions-package-ci-job.yml
+++ b/eng/pipelines/jobs/test-abstractions-package-ci-job.yml
@@ -19,6 +19,11 @@ parameters:
- Release
- Debug
+ # True to enable extra debug steps and logging.
+ - name: debug
+ type: boolean
+ default: false
+
# The prefix to prepend to the job's display name:
#
# [] Run Stress Tests
@@ -26,6 +31,17 @@ parameters:
- name: displayNamePrefix
type: string
+ # The verbosity level for the dotnet CLI commands.
+ - name: dotnetVerbosity
+ type: string
+ default: normal
+ values:
+ - quiet
+ - minimal
+ - normal
+ - detailed
+ - diagnostic
+
# The suffix to append to the job name.
- name: jobNameSuffix
type: string
@@ -44,17 +60,6 @@ parameters:
- name: poolName
type: string
- # The verbosity level for the dotnet CLI commands.
- - name: verbosity
- type: string
- default: normal
- values:
- - quiet
- - minimal
- - normal
- - detailed
- - diagnostic
-
# The pool VM image to use.
- name: vmImage
type: string
@@ -83,13 +88,14 @@ jobs:
# dotnet CLI arguments common to all commands.
- name: commonArguments
value: >-
- --verbosity ${{ parameters.verbosity }}
+ --verbosity ${{ parameters.dotnetVerbosity }}
# dotnet CLI arguments for build/test/pack commands
- name: buildArguments
value: >-
$(commonArguments)
--configuration ${{ parameters.buildConfiguration }}
+ -p:ForceMdsAssemblyNameSuffix=true
# Explicitly unset the $PLATFORM environment variable that is set by the
# 'ADO Build properties' Library in the ADO SqlClientDrivers public project.
@@ -115,6 +121,11 @@ jobs:
steps:
+ # Emit environment variables if debug is enabled.
+ - ${{ if eq(parameters.debug, true) }}:
+ - pwsh: 'Get-ChildItem Env: | Sort-Object Name'
+ displayName: '[Debug] Print Environment Variables'
+
# Install the .NET 9.0 SDK.
- task: UseDotNet@2
displayName: Install .NET 9.0 SDK
@@ -135,18 +146,18 @@ jobs:
# We use the 'custom' command because the DotNetCoreCLI@2 task doesn't
# support all of our argument combinations for the different build steps.
- # Restore the solution.
+ # Restore the project.
- task: DotNetCoreCLI@2
- displayName: Restore Solution
+ displayName: Restore Project
inputs:
command: custom
custom: restore
projects: $(project)
arguments: $(commonArguments)
- # Build the solution.
+ # Build the project.
- task: DotNetCoreCLI@2
- displayName: Build Solution
+ displayName: Build Project
inputs:
command: custom
custom: build
diff --git a/eng/pipelines/jobs/test-azure-package-ci-job.yml b/eng/pipelines/jobs/test-azure-package-ci-job.yml
index 8e1b8fc6db..f9b8947a24 100644
--- a/eng/pipelines/jobs/test-azure-package-ci-job.yml
+++ b/eng/pipelines/jobs/test-azure-package-ci-job.yml
@@ -32,6 +32,11 @@ parameters:
- Release
- Debug
+ # True to enable extra debug steps and logging.
+ - name: debug
+ type: boolean
+ default: false
+
# The prefix to prepend to the job's display name:
#
# [] Run Stress Tests
@@ -39,10 +44,34 @@ parameters:
- name: displayNamePrefix
type: string
+ # The verbosity level for the dotnet CLI commands.
+ - name: dotnetVerbosity
+ type: string
+ default: normal
+ values:
+ - quiet
+ - minimal
+ - normal
+ - detailed
+ - diagnostic
+
# The suffix to append to the job name.
- name: jobNameSuffix
type: string
+ # The name of the MDS pipeline artifact to download.
+ #
+ # This is used when the referenceType is 'Package'.
+ - name: mdsArtifactName
+ type: string
+ default: MDS.Artifact
+
+ # The MDS package verion to depend on.
+ #
+ # This is used when the referenceType is 'Package'.
+ - name: mdsPackageVersion
+ type: string
+
# The list of .NET Framework runtimes to test against.
- name: netFrameworkRuntimes
type: object
@@ -68,17 +97,6 @@ parameters:
- Package
- Project
- # The verbosity level for the dotnet CLI commands.
- - name: verbosity
- type: string
- default: normal
- values:
- - quiet
- - minimal
- - normal
- - detailed
- - diagnostic
-
# The pool VM image to use.
- name: vmImage
type: string
@@ -107,9 +125,11 @@ jobs:
# dotnet CLI arguments common to all commands.
- name: commonArguments
value: >-
- --verbosity ${{ parameters.verbosity }}
+ --verbosity ${{ parameters.dotnetVerbosity }}
-p:ReferenceType=${{ parameters.referenceType }}
+ -p:ForceMdsAssemblyNameSuffix=true
-p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }}
+ -p:MdsPackageVersion=${{ parameters.mdsPackageVersion }}
# dotnet CLI arguments for build/test/pack commands
- name: buildArguments
@@ -141,6 +161,11 @@ jobs:
steps:
+ # Emit environment variables if debug is enabled.
+ - ${{ if eq(parameters.debug, true) }}:
+ - pwsh: 'Get-ChildItem Env: | Sort-Object Name'
+ displayName: '[Debug] Print Environment Variables'
+
# We have a few extra steps for Package reference builds.
- ${{ if eq(parameters.referenceType, 'Package') }}:
@@ -151,6 +176,15 @@ jobs:
artifactName: ${{ parameters.abstractionsArtifactName }}
targetPath: $(Build.SourcesDirectory)/packages
+ # Download the MDS package artifacts into packages/.
+ #
+ # The Azure project doesn't depend on MDS, but the test project does.
+ - task: DownloadPipelineArtifact@2
+ displayName: Download MDS Package Artifact
+ inputs:
+ artifactName: ${{ parameters.mdsArtifactName }}
+ targetPath: $(Build.SourcesDirectory)/packages
+
# Use the local NuGet.config that references the packages/ directory.
- pwsh: cp $(Build.SourcesDirectory)/NuGet.config.local $(Build.SourcesDirectory)/NuGet.config
displayName: Use local NuGet.config
@@ -172,32 +206,87 @@ jobs:
# The Windows agent images include a suitable .NET Framework runtime, so
# we don't have to install one explicitly.
+ # Setup the test config file.
+ #
+ # This must be done before building the project. This template updates
+ # the sample config file, which is then copied into place by the build.
+ #
+ - template: ../common/templates/steps/update-config-file-step.yml@self
+ parameters:
+ debug: ${{ parameters.debug }}
+
+ # The config.json file has many options, but only some of them are
+ # used by the Azure package tests. We only specify the ones that are
+ # necessary here.
+
+ AADServicePrincipalId: $(AADServicePrincipalId)
+ AzureKeyVaultTenantId: $(AzureKeyVaultTenantId)
+ # macOS doesn't support managed identities.
+ ManagedIdentitySupported: ${{ not(eq(parameters.vmImage, 'macos-latest')) }}
+ SupportsIntegratedSecurity: ${{ eq(variables['SupportsIntegratedSecurity'], 'true') }}
+ TCPConnectionString: $(AZURE_DB_TCP_CONN_STRING)
+ UserManagedIdentityClientId: $(UserManagedIdentityClientId)
+ WorkloadIdentityFederationServiceConnectionId: $(WorkloadIdentityFederationServiceConnectionId)
+ # Note: Using the isFork variable to determine if secrets are
+ # available is not ideal since it's an indirect association. But
+ # everything else (referencing secret variables various ways to detect
+ # if they were present) won't run consistently across forks and
+ # non-forks.
+ ${{ if eq(variables['system.pullRequest.isFork'], 'False') }}:
+ AADPasswordConnectionString: $(AAD_PASSWORD_CONN_STR)
+ AADServicePrincipalSecret: $(AADServicePrincipalSecret)
+
# We use the 'custom' command because the DotNetCoreCLI@2 task doesn't
# support all of our argument combinations for the different build steps.
- # Restore the solution.
+ # Restore the project.
- task: DotNetCoreCLI@2
- displayName: Restore Solution
+ displayName: Restore Project
inputs:
command: custom
custom: restore
projects: $(project)
arguments: $(commonArguments)
- # Build the solution.
+ # Build the project.
- task: DotNetCoreCLI@2
- displayName: Build Solution
+ displayName: Build Project
inputs:
command: custom
custom: build
projects: $(project)
arguments: $(buildArguments) --no-restore
+ # List the DLLs in the output directory for debugging purposes.
+ - ${{ if eq(parameters.debug, true) }}:
+ - pwsh: |
+ Get-ChildItem `
+ -Path "src/Microsoft.Data.SqlClient.Extensions/Azure/test/bin/${{ parameters.buildConfiguration }}" `
+ -Recurse
+ displayName: '[Debug] List Output DLLs'
+
# Run the tests for each .NET runtime.
- ${{ each runtime in parameters.netRuntimes }}:
- task: DotNetCoreCLI@2
displayName: Test [${{ runtime }}]
+ env:
+ # Many of our tests require access to Azure resources that are
+ # currently only granted by agents running our custom ADO 1ES
+ # images in our ADO pools.
+ ${{ if startsWith(parameters.poolName, 'ADO-') }}:
+ ADO_POOL: 1
+ # When using connectedServiceName below, the DotNetCoreCLI task
+ # needs the system access token to be injected as this environment
+ # variable.
+ SYSTEM_ACCESSTOKEN: $(System.AccessToken)
+ ${{ if eq(parameters.debug, true) }}:
+ TEST_DEBUG_EMIT: 1
inputs:
+ # The tests need to access Azure resources, which is achieved via
+ # this service connection. See:
+ #
+ # https://sqlclientdrivers.visualstudio.com/public/_settings/adminservices?resourceId=ec9623b2-829c-497f-ae1f-7461766f9a9c
+ connectedServiceName: dotnetMSI-managed-identity
command: custom
custom: test
projects: $(project)
@@ -207,7 +296,20 @@ jobs:
- ${{ each runtime in parameters.netFrameworkRuntimes }}:
- task: DotNetCoreCLI@2
displayName: Test [${{ runtime }}]
+ env:
+ # Many of our tests require access to Azure resources that are
+ # currently only granted by agents running our custom ADO-CI 1ES
+ # images in our ADO pool.
+ ${{ if startsWith(parameters.poolName, 'ADO-CI') }}:
+ ADO_POOL: 1
+ # When using connectedServiceName below, the DotNetCoreCLI task
+ # needs the system access token to be injected as this environment
+ # variable.
+ SYSTEM_ACCESSTOKEN: $(System.AccessToken)
+ ${{ if eq(parameters.debug, true) }}:
+ TEST_DEBUG_EMIT: 1
inputs:
+ connectedServiceName: dotnetMSI-managed-identity
command: custom
custom: test
projects: $(project)
diff --git a/eng/pipelines/sqlclient-pr-package-ref-pipeline.yml b/eng/pipelines/sqlclient-pr-package-ref-pipeline.yml
index f44aaf9910..41786ed2d8 100644
--- a/eng/pipelines/sqlclient-pr-package-ref-pipeline.yml
+++ b/eng/pipelines/sqlclient-pr-package-ref-pipeline.yml
@@ -111,7 +111,7 @@ extends:
parameters:
buildConfiguration: ${{ parameters.buildConfiguration }}
buildPlatforms: ${{ parameters.buildPlatforms }}
- buildType: Package
+ referenceType: Package
codeCovTargetFrameworks: ${{ parameters.codeCovTargetFrameworks }}
debug: ${{ parameters.debug }}
enableStressTests: ${{ parameters.enableStressTests }}
diff --git a/eng/pipelines/sqlclient-pr-project-ref-pipeline.yml b/eng/pipelines/sqlclient-pr-project-ref-pipeline.yml
index e835a5db78..3e4eb85a9e 100644
--- a/eng/pipelines/sqlclient-pr-project-ref-pipeline.yml
+++ b/eng/pipelines/sqlclient-pr-project-ref-pipeline.yml
@@ -111,7 +111,7 @@ extends:
parameters:
buildConfiguration: ${{ parameters.buildConfiguration }}
buildPlatforms: ${{ parameters.buildPlatforms }}
- buildType: Project
+ referenceType: Project
codeCovTargetFrameworks: ${{ parameters.codeCovTargetFrameworks }}
debug: ${{ parameters.debug }}
enableStressTests: ${{ parameters.enableStressTests }}
diff --git a/eng/pipelines/stages/build-abstractions-package-ci-stage.yml b/eng/pipelines/stages/build-abstractions-package-ci-stage.yml
index 0d84dc6ccb..201840fac1 100644
--- a/eng/pipelines/stages/build-abstractions-package-ci-stage.yml
+++ b/eng/pipelines/stages/build-abstractions-package-ci-stage.yml
@@ -25,15 +25,15 @@
parameters:
- # The version to apply to the NuGet package and DLLs.
- - name: abstractionsPackageVersion
- type: string
-
# The name of the pipeline artifact to publish.
- - name: artifactName
+ - name: abstractionsArtifactName
type: string
default: Abstractions.Artifact
+ # The version to apply to the NuGet package and DLLs.
+ - name: abstractionsPackageVersion
+ type: string
+
# The type of build to produce (Release or Debug)
- name: buildConfiguration
type: string
@@ -42,8 +42,13 @@ parameters:
- Release
- Debug
+ # True to enable extra debug steps and logging.
+ - name: debug
+ type: boolean
+ default: false
+
# The verbosity level for the dotnet CLI commands.
- - name: verbosity
+ - name: dotnetVerbosity
type: string
default: normal
values:
@@ -65,55 +70,59 @@ stages:
- template: ../jobs/test-abstractions-package-ci-job.yml@self
parameters:
- jobNameSuffix: linux
+ buildConfiguration: ${{ parameters.buildConfiguration }}
+ debug: ${{ parameters.debug }}
displayNamePrefix: Linux
+ dotnetVerbosity: ${{ parameters.dotnetVerbosity }}
+ jobNameSuffix: linux
+ netFrameworkRuntimes: []
+ netRuntimes: [net8.0, net9.0]
poolName: Azure Pipelines
vmImage: ubuntu-latest
- buildConfiguration: ${{ parameters.buildConfiguration }}
- netRuntimes: [net8.0, net9.0]
- netFrameworkRuntimes: []
- verbosity: ${{ parameters.verbosity }}
# ------------------------------------------------------------------------
# Build and test on Windows
- template: ../jobs/test-abstractions-package-ci-job.yml@self
parameters:
- jobNameSuffix: windows
+ buildConfiguration: ${{ parameters.buildConfiguration }}
+ debug: ${{ parameters.debug }}
displayNamePrefix: Win
+ dotnetVerbosity: ${{ parameters.dotnetVerbosity }}
+ jobNameSuffix: windows
+ netFrameworkRuntimes: [net462]
+ netRuntimes: [net8.0, net9.0]
poolName: Azure Pipelines
vmImage: windows-latest
- buildConfiguration: ${{ parameters.buildConfiguration }}
- netRuntimes: [net8.0, net9.0]
- netFrameworkRuntimes: [net462]
- verbosity: ${{ parameters.verbosity }}
# ------------------------------------------------------------------------
# Build and test on macOS.
- template: ../jobs/test-abstractions-package-ci-job.yml
parameters:
- jobNameSuffix: macos
+ buildConfiguration: ${{ parameters.buildConfiguration }}
+ debug: ${{ parameters.debug }}
displayNamePrefix: macOS
+ dotnetVerbosity: ${{ parameters.dotnetVerbosity }}
+ jobNameSuffix: macos
+ netFrameworkRuntimes: []
+ netRuntimes: [net8.0, net9.0]
poolName: Azure Pipelines
vmImage: macos-latest
- buildConfiguration: ${{ parameters.buildConfiguration }}
- netRuntimes: [net8.0, net9.0]
- netFrameworkRuntimes: []
- verbosity: ${{ parameters.verbosity }}
# ------------------------------------------------------------------------
# Create and publish the NuGet package.
- template: ../jobs/pack-abstractions-package-ci-job.yml@self
parameters:
- artifactName: ${{ parameters.artifactName }}
- buildConfiguration: ${{ parameters.buildConfiguration }}
+ abstractionsArtifactName: ${{ parameters.abstractionsArtifactName }}
abstractionsPackageVersion: ${{ parameters.abstractionsPackageVersion }}
- verbosity: ${{ parameters.verbosity }}
+ buildConfiguration: ${{ parameters.buildConfiguration }}
+ debug: ${{ parameters.debug }}
dependsOn:
# We depend on all of the test jobs to ensure the tests pass before
# producing the NuGet package.
- test_abstractions_package_job_linux
- test_abstractions_package_job_windows
- test_abstractions_package_job_macos
+ dotnetVerbosity: ${{ parameters.dotnetVerbosity }}
diff --git a/eng/pipelines/stages/build-azure-package-ci-stage.yml b/eng/pipelines/stages/build-azure-package-ci-stage.yml
index 90beb30caf..87d99b6ae2 100644
--- a/eng/pipelines/stages/build-azure-package-ci-stage.yml
+++ b/eng/pipelines/stages/build-azure-package-ci-stage.yml
@@ -38,6 +38,19 @@ parameters:
- name: abstractionsPackageVersion
type: string
+ # The name of the pool to use for jobs that require customized VM images.
+ - name: adoPoolName
+ type: string
+ # This variable should be defined in AzureDevOps Library variable groups,
+ # for both the Public and ADO.Net projects.
+ #
+ # Any pool specified here must contain images with the following names:
+ #
+ # - ADO-UB22-SQL22
+ # - ADO-CI-Win11
+ #
+ default: $(ci_var_defaultPoolName)
+
# The name of the pipeline artifact to publish.
- name: azureArtifactName
type: string
@@ -47,6 +60,12 @@ parameters:
- name: azurePackageVersion
type: string
+ # The name of the general Azure pool to use for jobs that don't require
+ # customized VM images.
+ - name: azurePoolName
+ type: string
+ default: Azure Pipelines
+
# The type of build to produce (Release or Debug)
- name: buildConfiguration
type: string
@@ -55,10 +74,39 @@ parameters:
- Release
- Debug
+ # True to enable extra debug steps and logging.
+ - name: debug
+ type: boolean
+ default: false
+
# The stages we depend on, if any.
- name: dependsOn
type: object
default: []
+
+ # The dotnet CLI verbosity to use.
+ - name: dotnetVerbosity
+ type: string
+ default: normal
+ values:
+ - quiet
+ - minimal
+ - normal
+ - detailed
+ - diagnostic
+
+ # The name of the MDS pipeline artifact to download.
+ #
+ # This is used when the referenceType is 'Package'.
+ - name: mdsArtifactName
+ type: string
+ default: MDS.Artifact
+
+ # The MDS package verion to depend on.
+ #
+ # This is used when the referenceType is 'Package'.
+ - name: mdsPackageVersion
+ type: string
# The reference type to use:
#
@@ -71,17 +119,6 @@ parameters:
- Package
- Project
- # The dotnet CLI verbosity to use.
- - name: verbosity
- type: string
- default: normal
- values:
- - quiet
- - minimal
- - normal
- - detailed
- - diagnostic
-
stages:
- stage: build_azure_package_stage
@@ -94,54 +131,104 @@ stages:
# ------------------------------------------------------------------------
# Build and test on Linux.
+ # Use the Azure Pipelines pool for basic testing.
- template: ../jobs/test-azure-package-ci-job.yml@self
parameters:
abstractionsArtifactName: ${{ parameters.abstractionsArtifactName }}
abstractionsPackageVersion: ${{ parameters.abstractionsPackageVersion }}
buildConfiguration: ${{ parameters.buildConfiguration }}
+ debug: ${{ parameters.debug }}
displayNamePrefix: Linux
- jobNameSuffix: linux
+ dotnetVerbosity: ${{ parameters.dotnetVerbosity }}
+ jobNameSuffix: linux_basic
+ mdsArtifactName: MDS.Artifact
+ mdsPackageVersion: ${{ parameters.mdsPackageVersion }}
netFrameworkRuntimes: []
netRuntimes: [net8.0, net9.0]
- poolName: Azure Pipelines
+ poolName: ${{ parameters.azurePoolName }}
referenceType: ${{ parameters.referenceType }}
- verbosity: ${{ parameters.verbosity }}
vmImage: ubuntu-latest
+ # Use our 1ES ADO pool for comprehensive testing.
+ - template: ../jobs/test-azure-package-ci-job.yml@self
+ parameters:
+ abstractionsArtifactName: ${{ parameters.abstractionsArtifactName }}
+ abstractionsPackageVersion: ${{ parameters.abstractionsPackageVersion }}
+ buildConfiguration: ${{ parameters.buildConfiguration }}
+ debug: ${{ parameters.debug }}
+ displayNamePrefix: Linux
+ dotnetVerbosity: ${{ parameters.dotnetVerbosity }}
+ jobNameSuffix: linux_comprehensive
+ mdsArtifactName: MDS.Artifact
+ mdsPackageVersion: ${{ parameters.mdsPackageVersion }}
+ netFrameworkRuntimes: []
+ netRuntimes: [net8.0, net9.0]
+ poolName: ${{ parameters.adoPoolName }}
+ referenceType: ${{ parameters.referenceType }}
+ vmImage: ADO-UB22-SQL22
+
# ------------------------------------------------------------------------
# Build and test on Windows
+ # Use the Azure Pipelines pool for basic testing.
- template: ../jobs/test-azure-package-ci-job.yml@self
parameters:
abstractionsArtifactName: ${{ parameters.abstractionsArtifactName }}
abstractionsPackageVersion: ${{ parameters.abstractionsPackageVersion }}
buildConfiguration: ${{ parameters.buildConfiguration }}
+ debug: ${{ parameters.debug }}
displayNamePrefix: Win
- jobNameSuffix: windows
+ dotnetVerbosity: ${{ parameters.dotnetVerbosity }}
+ jobNameSuffix: windows_basic
+ mdsArtifactName: MDS.Artifact
+ mdsPackageVersion: ${{ parameters.mdsPackageVersion }}
netFrameworkRuntimes: [net462]
netRuntimes: [net8.0, net9.0]
- poolName: Azure Pipelines
+ poolName: ${{ parameters.azurePoolName }}
referenceType: ${{ parameters.referenceType }}
- verbosity: ${{ parameters.verbosity }}
vmImage: windows-latest
+ # Use our 1ES ADO pool for comprehensive testing.
+ - template: ../jobs/test-azure-package-ci-job.yml@self
+ parameters:
+ abstractionsArtifactName: ${{ parameters.abstractionsArtifactName }}
+ abstractionsPackageVersion: ${{ parameters.abstractionsPackageVersion }}
+ buildConfiguration: ${{ parameters.buildConfiguration }}
+ debug: ${{ parameters.debug }}
+ displayNamePrefix: Win
+ dotnetVerbosity: ${{ parameters.dotnetVerbosity }}
+ jobNameSuffix: windows_comprehensive
+ mdsArtifactName: MDS.Artifact
+ mdsPackageVersion: ${{ parameters.mdsPackageVersion }}
+ netFrameworkRuntimes: [net462]
+ netRuntimes: [net8.0, net9.0]
+ poolName: ${{ parameters.adoPoolName }}
+ referenceType: ${{ parameters.referenceType }}
+ vmImage: ADO-CI-Win11
+
# ------------------------------------------------------------------------
# Build and test on macOS.
+ # Use the Azure Pipelines pool for basic testing.
- template: ../jobs/test-azure-package-ci-job.yml
parameters:
abstractionsArtifactName: ${{ parameters.abstractionsArtifactName }}
abstractionsPackageVersion: ${{ parameters.abstractionsPackageVersion }}
buildConfiguration: ${{ parameters.buildConfiguration }}
+ debug: ${{ parameters.debug }}
displayNamePrefix: macOS
+ dotnetVerbosity: ${{ parameters.dotnetVerbosity }}
jobNameSuffix: macos
+ mdsArtifactName: MDS.Artifact
+ mdsPackageVersion: ${{ parameters.mdsPackageVersion }}
netFrameworkRuntimes: []
netRuntimes: [net8.0, net9.0]
- poolName: Azure Pipelines
+ poolName: ${{ parameters.azurePoolName }}
referenceType: ${{ parameters.referenceType }}
- verbosity: ${{ parameters.verbosity }}
vmImage: macos-latest
+ # We do not currently have any images in our 1ES ADO pools for macOS.
+
# ------------------------------------------------------------------------
# Create and publish the NuGet package.
@@ -152,11 +239,14 @@ stages:
azureArtifactName: ${{ parameters.azureArtifactName }}
azurePackageVersion: ${{ parameters.abstractionsPackageVersion }}
buildConfiguration: ${{ parameters.buildConfiguration }}
+ debug: ${{ parameters.debug }}
dependsOn:
# We depend on all of the test jobs to ensure the tests pass before
# producing the NuGet package.
- - test_azure_package_job_linux
- - test_azure_package_job_windows
+ - test_azure_package_job_linux_basic
+ - test_azure_package_job_linux_comprehensive
+ - test_azure_package_job_windows_basic
+ - test_azure_package_job_windows_comprehensive
- test_azure_package_job_macos
+ dotnetVerbosity: ${{ parameters.dotnetVerbosity }}
referenceType: ${{ parameters.referenceType }}
- verbosity: ${{ parameters.verbosity }}
diff --git a/eng/pipelines/stages/stress-tests-ci-stage.yml b/eng/pipelines/stages/stress-tests-ci-stage.yml
index b7cea84b82..1204e0b4bd 100644
--- a/eng/pipelines/stages/stress-tests-ci-stage.yml
+++ b/eng/pipelines/stages/stress-tests-ci-stage.yml
@@ -36,12 +36,17 @@ parameters:
type: object
default: []
- # The name of the pipeline artifact to download that contains the MDS package
- # to stress test.
- - name: pipelineArtifactName
- displayName: Pipeline Artifact Name
+ # The verbosity level for the dotnet CLI commands.
+ - name: dotnetVerbosity
+ displayName: dotnet CLI verbosity
type: string
- default: Artifacts
+ default: normal
+ values:
+ - quiet
+ - minimal
+ - normal
+ - detailed
+ - diagnostic
# The MDS package version to stress test. This version must be available in
# one of the configured NuGet sources.
@@ -50,29 +55,24 @@ parameters:
type: string
default: ''
- # The list of .NET runtimes to test against.
- - name: netTestRuntimes
- displayName: .NET Test Runtimes
- type: object
- default: [net8.0, net9.0]
-
# The list of .NET Framework runtimes to test against.
- name: netFrameworkTestRuntimes
displayName: .NET Framework Test Runtimes
type: object
default: [net462, net47, net471, net472, net48, net481]
- # The verbosity level for the dotnet CLI commands.
- - name: verbosity
- displayName: Dotnet CLI verbosity
+ # The list of .NET runtimes to test against.
+ - name: netTestRuntimes
+ displayName: .NET Test Runtimes
+ type: object
+ default: [net8.0, net9.0]
+
+ # The name of the pipeline artifact to download that contains the MDS package
+ # to stress test.
+ - name: pipelineArtifactName
+ displayName: Pipeline Artifact Name
type: string
- default: normal
- values:
- - quiet
- - minimal
- - normal
- - detailed
- - diagnostic
+ default: Artifacts
stages:
- stage: run_stress_tests_stage
@@ -96,7 +96,7 @@ stages:
# dotnet CLI arguments common to all commands.
- name: commonArguments
value: >-
- --verbosity ${{parameters.verbosity}}
+ --verbosity ${{parameters.dotnetVerbosity}}
--artifacts-path $(dotnetArtifactsDir)
-p:MdsPackageVersion=${{parameters.mdsPackageVersion}}
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index d79b9a547f..2ac32b09f9 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -17,7 +17,7 @@
See the BUILDGUIDE.md for more details.
-->
- Project
+ Project
$([System.IO.Path]::Combine('$(IntermediateOutputPath)','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)'))
@@ -153,4 +153,43 @@
13
+
+
+
+ false
+ true
+
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Abstractions.csproj b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Abstractions.csproj
index 9436e9067b..ba7dfd2a48 100644
--- a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Abstractions.csproj
+++ b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Abstractions.csproj
@@ -42,6 +42,12 @@
$(AbstractionsPackageVersion)
$(Artifacts)/doc/$(TargetFramework)/$(AssemblyName).xml
+
+
+ $(DefineConstants);APPLY_MDS_ASSEMBLY_NAME_SUFFIX
@@ -82,5 +88,5 @@
-
+
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/SqlAuthenticationProviderInternal.cs b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/SqlAuthenticationProviderInternal.cs
index c14a758f76..7e4c5b0645 100644
--- a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/SqlAuthenticationProviderInternal.cs
+++ b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/SqlAuthenticationProviderInternal.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Reflection;
+using System.Runtime.InteropServices;
namespace Microsoft.Data.SqlClient;
@@ -31,10 +32,23 @@ private static class Internal
///
static Internal()
{
+ // Choose the MDS assembly name based on the build configuration and
+ // runtime environment. See the top-level Directory.Build.props for
+ // more information.
+ string assemblyName = "Microsoft.Data.SqlClient";
+ #if (APPLY_MDS_ASSEMBLY_NAME_SUFFIX)
+ if (RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework"))
+ {
+ assemblyName += ".NetFx";
+ }
+ else
+ {
+ assemblyName += ".NetCore";
+ }
+ #endif
+
// If the MDS package is present, load its
// SqlAuthenticationProviderManager class and get/set methods.
- const string assemblyName = "Microsoft.Data.SqlClient";
-
try
{
// Try to load the MDS assembly.
@@ -42,35 +56,21 @@ static Internal()
if (assembly is null)
{
- // TODO: Logging
- // SqlClientEventSource.Log.TryTraceEvent(
- // nameof(SqlAuthenticationProviderManager) +
- // $": Azure extension assembly={assemblyName} not found; " +
- // "no default provider installed");
+ Log($"MDS assembly={assemblyName} not found; " +
+ "Get/SetProvider() will not function");
return;
}
// TODO(ADO-39845): Verify the assembly is signed by us?
- // TODO: Logging
- // SqlClientEventSource.Log.TryTraceEvent(
- // nameof(SqlAuthenticationProviderManager) +
- // $": Azure extension assembly={assemblyName} found; " +
- // "attempting to set as default provider for all Active " +
- // "Directory authentication methods");
-
// Look for the manager class.
const string className = "Microsoft.Data.SqlClient.SqlAuthenticationProviderManager";
var manager = assembly.GetType(className);
if (manager is null)
{
- // TODO: Logging
- // SqlClientEventSource.Log.TryTraceEvent(
- // nameof(SqlAuthenticationProviderManager) +
- // $": Azure extension does not contain class={className}; " +
- // "no default Active Directory provider installed");
-
+ Log($"MDS auth manager manager class={className} not found; " +
+ "Get/SetProvider() will not function");
return;
}
@@ -78,15 +78,22 @@ static Internal()
_getProvider = manager.GetMethod(
"GetProvider",
BindingFlags.NonPublic | BindingFlags.Static);
+
+ if (_getProvider is null)
+ {
+ Log($"MDS GetProvider() method not found; " +
+ "GetProvider() will not function");
+ }
+
_setProvider = manager.GetMethod(
"SetProvider",
BindingFlags.NonPublic | BindingFlags.Static);
-
- // TODO: Logging
- // SqlClientEventSource.Log.TryTraceEvent(
- // nameof(SqlAuthenticationProviderManager) +
- // $": Azure extension class={className} installed as " +
- // "provider for all Active Directory authentication methods");
+
+ if (_setProvider is null)
+ {
+ Log($"MDS SetProvider() method not found; " +
+ "SetProvider() will not function");
+ }
}
// All of these exceptions mean we couldn't find the get/set
// methods.
@@ -96,12 +103,8 @@ ex is BadImageFormatException ||
ex is FileLoadException ||
ex is FileNotFoundException)
{
- // TODO: Logging
- // SqlClientEventSource.Log.TryTraceEvent(
- // nameof(SqlAuthenticationProviderManager) +
- // $": Azure extension assembly={assemblyName} not found or " +
- // "not usable; no default provider installed; " +
- // $"{ex.GetType().Name}: {ex.Message}");
+ Log($"MDS assembly={assemblyName} not found or not usable; " +
+ $"Get/SetProvider() will not function: {ex} ");
}
// Any other exceptions are fatal.
}
@@ -136,6 +139,8 @@ ex is MethodAccessException ||
ex is NotSupportedException ||
ex is TargetInvocationException)
{
+ Log($"GetProvider() invocation failed: " +
+ $"{ex.GetType().Name}: {ex.Message}");
return null;
}
}
@@ -171,6 +176,8 @@ internal static bool SetProvider(
if (!result.HasValue)
{
+ Log($"SetProvider() invocation returned null; " +
+ "translating to false");
return false;
}
@@ -183,8 +190,16 @@ ex is MethodAccessException ||
ex is NotSupportedException ||
ex is TargetInvocationException)
{
+ Log($"SetProvider() invocation failed: " +
+ $"{ex.GetType().Name}: {ex.Message}");
return false;
}
}
+
+ private static void Log(string message)
+ {
+ // TODO(ADO-39080): Convert to proper logging.
+ Console.WriteLine($"SqlAuthenticationProvider.Internal(): {message}");
+ }
}
}
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/Abstractions.Test.csproj b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/Abstractions.Test.csproj
index 053804342e..2f7c023e95 100644
--- a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/Abstractions.Test.csproj
+++ b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/Abstractions.Test.csproj
@@ -9,6 +9,14 @@
Microsoft.Data.SqlClient.Extensions.Abstractions.Test
+
+
+ $(DefineConstants);APPLY_MDS_ASSEMBLY_NAME_SUFFIX
+
+
@@ -24,4 +32,11 @@
+
+
+ PreserveNewest
+ xunit.runner.json
+
+
+
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/SqlAuthenticationProviderTest.cs b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/SqlAuthenticationProviderTest.cs
index 07e2b40078..7cd067aca3 100644
--- a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/SqlAuthenticationProviderTest.cs
+++ b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/SqlAuthenticationProviderTest.cs
@@ -8,14 +8,23 @@ namespace Microsoft.Data.SqlClient.Extensions.Abstractions.Test;
public class SqlAuthenticationProviderTest
{
+ // Choose the MDS assembly name based on the build environment.
+ // See the top-level Directory.Build.props for more information.
+ #if (APPLY_MDS_ASSEMBLY_NAME_SUFFIX && NET)
+ const string assemblyName = "Microsoft.Data.SqlClient.NetCore";
+ #elif (APPLY_MDS_ASSEMBLY_NAME_SUFFIX && NETFRAMEWORK)
+ const string assemblyName = "Microsoft.Data.SqlClient.NetFx";
+ #else
+ const string assemblyName = "Microsoft.Data.SqlClient";
+ #endif
+
///
/// Construct to confirm preconditions.
///
public SqlAuthenticationProviderTest()
{
// Confirm that the MDS assembly is indeed not present.
- Assert.Throws(
- () => Assembly.Load("Microsoft.Data.SqlClient"));
+ Assert.Throws(() => Assembly.Load(assemblyName));
}
#region Tests
@@ -74,6 +83,8 @@ public void SetProvider_NoMdsAssembly(SqlAuthenticationMethod method)
///
/// A dummy provider that supports all authentication methods.
///
+
+ // A dummy provider that supports all authentication methods.
private sealed class Provider : SqlAuthenticationProvider
{
///
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Azure/test/AADAuthenticationTests.cs b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/AADAuthenticationTests.cs
new file mode 100644
index 0000000000..b7f23c1217
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/AADAuthenticationTests.cs
@@ -0,0 +1,33 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace Microsoft.Data.SqlClient.Extensions.Azure.Test;
+
+// These tests were moved from MDS FunctionalTests AADAuthenticationTests.cs.
+public class AADAuthenticationTests
+{
+ [Fact]
+ public void CustomActiveDirectoryProviderTest()
+ {
+ SqlAuthenticationProvider authProvider = new ActiveDirectoryAuthenticationProvider(static (result) => Task.CompletedTask);
+ SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow, authProvider);
+ Assert.Same(authProvider, SqlAuthenticationProvider.GetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow));
+ }
+
+ [Fact]
+ public void CustomActiveDirectoryProviderTest_AppClientId()
+ {
+ SqlAuthenticationProvider authProvider = new ActiveDirectoryAuthenticationProvider(Guid.NewGuid().ToString());
+ SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow, authProvider);
+ Assert.Same(authProvider, SqlAuthenticationProvider.GetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow));
+ }
+
+ [Fact]
+ public void CustomActiveDirectoryProviderTest_AppClientId_DeviceFlowCallback()
+ {
+ SqlAuthenticationProvider authProvider = new ActiveDirectoryAuthenticationProvider(static (result) => Task.CompletedTask, Guid.NewGuid().ToString());
+ SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow, authProvider);
+ Assert.Same(authProvider, SqlAuthenticationProvider.GetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow));
+ }
+}
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Azure/test/AADConnectionTest.cs b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/AADConnectionTest.cs
new file mode 100644
index 0000000000..19d22fbce9
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/AADConnectionTest.cs
@@ -0,0 +1,377 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.RegularExpressions;
+
+namespace Microsoft.Data.SqlClient.Extensions.Azure.Test;
+
+// These tests were migrated from MDS ManualTests AADConnectionTest.cs.
+public class AADConnectionTest
+{
+ [ConditionalFact(
+ typeof(Config),
+ nameof(Config.OnAdoPool),
+ nameof(Config.HasUserManagedIdentityClientId))]
+ public static void KustoDatabaseTest()
+ {
+ // This is a sample Kusto database that can be connected by any AD account.
+ using SqlConnection connection = new SqlConnection($"Data Source=help.kusto.windows.net; Authentication=Active Directory Default;Trust Server Certificate=True;User ID = {Config.UserManagedIdentityClientId};");
+ connection.Open();
+ Assert.True(connection.State == System.Data.ConnectionState.Open);
+ }
+
+ [ConditionalFact(
+ typeof(Config),
+ nameof(Config.HasPasswordConnectionString))]
+ public static void AADPasswordWithWrongPassword()
+ {
+ string[] credKeys = { "Password", "PWD" };
+ string connStr = RemoveKeysInConnStr(Config.PasswordConnectionString, credKeys) + "Password=TestPassword;";
+
+ Assert.Throws(() => ConnectAndDisconnect(connStr));
+
+ // We cannot verify error message with certainity as driver may cache token from other tests for current user
+ // and error message may change accordingly.
+ }
+
+ [ConditionalFact(
+ typeof(Config),
+ nameof(Config.HasPasswordConnectionString))]
+ public static void TestADPasswordAuthentication()
+ {
+ // Connect to Azure DB with password and retrieve user name.
+ using (SqlConnection conn = new SqlConnection(Config.PasswordConnectionString))
+ {
+ conn.Open();
+ using (SqlCommand sqlCommand = new SqlCommand
+ (
+ cmdText: $"SELECT SUSER_SNAME();",
+ connection: conn,
+ transaction: null
+ ))
+ {
+ string customerId = (string)sqlCommand.ExecuteScalar();
+ string expected = RetrieveValueFromConnStr(Config.PasswordConnectionString, new string[] { "User ID", "UID" });
+ Assert.Equal(expected, customerId);
+ }
+ }
+ }
+
+ [ConditionalFact(
+ typeof(Config),
+ nameof(Config.HasPasswordConnectionString))]
+ public static void EmptyPasswordInConnStrAADPassword()
+ {
+ // connection fails with expected error message.
+ string[] pwdKey = { "Password", "PWD" };
+ string connStr = RemoveKeysInConnStr(Config.PasswordConnectionString, pwdKey) + "Password=;";
+ SqlException e = Assert.Throws(() => ConnectAndDisconnect(connStr));
+
+ string? user = FetchKeyInConnStr(Config.PasswordConnectionString, new string[] { "User Id", "UID" });
+ string expectedMessage = string.Format("Failed to authenticate the user {0} in Active Directory (Authentication=ActiveDirectoryPassword).", user);
+ Assert.Contains(expectedMessage, e.Message);
+ }
+
+ [ConditionalFact(
+ typeof(Config),
+ nameof(Config.OnWindows),
+ nameof(Config.HasPasswordConnectionString))]
+ public static void EmptyCredInConnStrAADPassword()
+ {
+ // connection fails with expected error message.
+ string[] removeKeys = { "User ID", "Password", "UID", "PWD" };
+ string connStr = RemoveKeysInConnStr(Config.PasswordConnectionString, removeKeys) + "User ID=; Password=;";
+ SqlException e = Assert.Throws(() => ConnectAndDisconnect(connStr));
+
+ string expectedMessage = "Failed to authenticate the user in Active Directory (Authentication=ActiveDirectoryPassword).";
+ Assert.Contains(expectedMessage, e.Message);
+ }
+
+ [ConditionalFact(
+ typeof(Config),
+ nameof(Config.OnUnix),
+ nameof(Config.HasPasswordConnectionString))]
+ public static void EmptyCredInConnStrAADPasswordAnyUnix()
+ {
+ // connection fails with expected error message.
+ string[] removeKeys = { "User ID", "Password", "UID", "PWD" };
+ string connStr = RemoveKeysInConnStr(Config.PasswordConnectionString, removeKeys) + "User ID=; Password=;";
+ SqlException e = Assert.Throws(() => ConnectAndDisconnect(connStr));
+
+ string expectedMessage = "MSAL cannot determine the username (UPN) of the currently logged in user.For Integrated Windows Authentication and Username/Password flows, please use .WithUsername() before calling ExecuteAsync().";
+ Assert.Contains(expectedMessage, e.Message);
+ }
+
+ [ConditionalFact(
+ typeof(Config),
+ nameof(Config.HasPasswordConnectionString))]
+ public static void AADPasswordWithInvalidUser()
+ {
+ // connection fails with expected error message.
+ string[] removeKeys = { "User ID", "UID" };
+ string user = "testdotnet@domain.com";
+ string connStr = RemoveKeysInConnStr(Config.PasswordConnectionString, removeKeys) + $"User ID={user}";
+ SqlException e = Assert.Throws(() => ConnectAndDisconnect(connStr));
+
+ string expectedMessage = string.Format("Failed to authenticate the user {0} in Active Directory (Authentication=ActiveDirectoryPassword).", user);
+ Assert.Contains(expectedMessage, e.Message);
+ }
+
+ [ConditionalFact(
+ typeof(Config),
+ nameof(Config.HasPasswordConnectionString))]
+ public static void NoCredentialsActiveDirectoryPassword()
+ {
+ // test Passes with correct connection string.
+ ConnectAndDisconnect(Config.PasswordConnectionString);
+
+ // connection fails with expected error message.
+ string[] credKeys = { "User ID", "Password", "UID", "PWD" };
+ string connStrWithNoCred = RemoveKeysInConnStr(Config.PasswordConnectionString, credKeys);
+ InvalidOperationException e = Assert.Throws(() => ConnectAndDisconnect(connStrWithNoCred));
+
+ string expectedMessage = "Either Credential or both 'User ID' and 'Password' (or 'UID' and 'PWD') connection string keywords must be specified, if 'Authentication=Active Directory Password'.";
+ Assert.Contains(expectedMessage, e.Message);
+ }
+
+ [ConditionalFact(
+ typeof(Config),
+ nameof(Config.HasPasswordConnectionString),
+ nameof(Config.HasServicePrincipal))]
+ public static void NoCredentialsActiveDirectoryServicePrincipal()
+ {
+ // test Passes with correct connection string.
+ string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD" };
+ string connStr = RemoveKeysInConnStr(Config.PasswordConnectionString, removeKeys) +
+ $"Authentication=Active Directory Service Principal; User ID={Config.ServicePrincipalId}; PWD={Config.ServicePrincipalSecret};";
+ ConnectAndDisconnect(connStr);
+
+ // connection fails with expected error message.
+ string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" };
+ string connStrWithNoCred = RemoveKeysInConnStr(Config.PasswordConnectionString, credKeys) +
+ "Authentication=Active Directory Service Principal;";
+ InvalidOperationException e = Assert.Throws(() => ConnectAndDisconnect(connStrWithNoCred));
+
+ string expectedMessage = "Either Credential or both 'User ID' and 'Password' (or 'UID' and 'PWD') connection string keywords must be specified, if 'Authentication=Active Directory Service Principal'.";
+ Assert.Contains(expectedMessage, e.Message);
+ }
+
+ [ConditionalTheory(
+ typeof(Config),
+ nameof(Config.HasPasswordConnectionString),
+ nameof(Config.HasUserManagedIdentityClientId))]
+ [InlineData("2445343 2343253")]
+ [InlineData("2445343$#^@@%2343253")]
+ public static void ActiveDirectoryManagedIdentityWithInvalidUserIdMustFail(string userId)
+ {
+ // connection fails with expected error message.
+ string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" };
+ string connStrWithNoCred = RemoveKeysInConnStr(Config.PasswordConnectionString, credKeys) +
+ $"Authentication=Active Directory Managed Identity; User Id={userId}";
+
+ SqlException e = Assert.Throws(() => ConnectAndDisconnect(connStrWithNoCred));
+
+ Regex expected = new(
+ @"(\[Managed Identity\]|ManagedIdentityCredential) Authentication unavailable",
+ RegexOptions.IgnoreCase);
+
+ Assert.Matches(expected, e.GetBaseException().Message);
+ }
+
+ [ConditionalFact(
+ typeof(Config),
+ nameof(Config.OnAdoPool),
+ nameof(Config.HasPasswordConnectionString),
+ nameof(Config.HasUserManagedIdentityClientId))]
+ public static void ActiveDirectoryDefaultMustPass()
+ {
+ string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" };
+ string connStr = RemoveKeysInConnStr(Config.PasswordConnectionString, credKeys) +
+ $"Authentication=ActiveDirectoryDefault;User ID={Config.UserManagedIdentityClientId};";
+
+ // Connection should be established using Managed Identity by default.
+ ConnectAndDisconnect(connStr);
+ }
+
+ [ConditionalFact(
+ typeof(Config),
+ nameof(Config.HasIntegratedSecurityConnectionString),
+ nameof(Config.HasTcpConnectionString))]
+ public static void ADIntegratedUsingSSPI()
+ {
+ // test Passes with correct connection string.
+ string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD", "Trusted_Connection", "Integrated Security" };
+ string connStr = RemoveKeysInConnStr(Config.TcpConnectionString, removeKeys) +
+ $"Authentication=Active Directory Integrated;";
+ ConnectAndDisconnect(connStr);
+ }
+
+ [ConditionalFact(
+ typeof(Config),
+ nameof(Config.HasPasswordConnectionString),
+ nameof(Config.SupportsManagedIdentity),
+ nameof(Config.SupportsSystemAssignedManagedIdentity))]
+ public static void SystemAssigned_ManagedIdentityTest()
+ {
+ string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD" };
+ string connStr = RemoveKeysInConnStr(Config.PasswordConnectionString, removeKeys) +
+ $"Authentication=Active Directory Managed Identity;";
+ ConnectAndDisconnect(connStr);
+ }
+
+ [ConditionalFact(
+ typeof(Config),
+ nameof(Config.OnAdoPool),
+ nameof(Config.HasPasswordConnectionString),
+ nameof(Config.HasUserManagedIdentityClientId))]
+ public static void UserAssigned_ManagedIdentityTest()
+ {
+ string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD" };
+ string connStr = RemoveKeysInConnStr(Config.PasswordConnectionString, removeKeys) +
+ $"Authentication=Active Directory Managed Identity; User Id={Config.UserManagedIdentityClientId};";
+ ConnectAndDisconnect(connStr);
+ }
+
+ [ConditionalFact(
+ typeof(Config),
+ nameof(Config.HasTcpConnectionString),
+ nameof(Config.SupportsManagedIdentity),
+ nameof(Config.SupportsSystemAssignedManagedIdentity),
+ nameof(Config.IsAzureSqlServer))]
+ public static void Azure_SystemManagedIdentityTest()
+ {
+ string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD", "Trusted_Connection", "Integrated Security" };
+ string connectionString = RemoveKeysInConnStr(Config.TcpConnectionString, removeKeys)
+ + $"Authentication=Active Directory Managed Identity;";
+
+ using (SqlConnection conn = new SqlConnection(connectionString))
+ {
+ conn.Open();
+
+ Assert.True(conn.State == System.Data.ConnectionState.Open);
+ }
+ }
+
+ [ConditionalFact(
+ typeof(Config),
+ nameof(Config.HasTcpConnectionString),
+ nameof(Config.HasUserManagedIdentityClientId),
+ nameof(Config.SupportsManagedIdentity),
+ nameof(Config.SupportsSystemAssignedManagedIdentity),
+ nameof(Config.IsAzureSqlServer))]
+ public static void Azure_UserManagedIdentityTest()
+ {
+ string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD", "Trusted_Connection", "Integrated Security" };
+ string connectionString = RemoveKeysInConnStr(Config.TcpConnectionString, removeKeys)
+ + $"Authentication=Active Directory Managed Identity; User Id={Config.UserManagedIdentityClientId}";
+
+ using (SqlConnection conn = new SqlConnection(connectionString))
+ {
+ conn.Open();
+
+ Assert.True(conn.State == System.Data.ConnectionState.Open);
+ }
+ }
+
+ #region Helpers from AADConnectionTest.cs
+
+ private static void ConnectAndDisconnect(
+ string connectionString, SqlCredential? credential = null)
+ {
+ using SqlConnection conn = new(connectionString);
+
+ if (credential is not null)
+ {
+ conn.Credential = credential;
+ }
+
+ conn.Open();
+
+ Assert.True(conn.State == System.Data.ConnectionState.Open);
+ }
+
+ #endregion
+
+ #region Helpers from ManualTests DataTestUtility.cs
+
+ public static string RemoveKeysInConnStr(string connStr, string[] keysToRemove)
+ {
+ // tokenize connection string and remove input keys.
+ string res = "";
+ if (connStr != null && keysToRemove != null)
+ {
+ string[] keys = connStr.Split(';');
+ foreach (var key in keys)
+ {
+ if (!string.IsNullOrEmpty(key.Trim()))
+ {
+ bool removeKey = false;
+ foreach (var keyToRemove in keysToRemove)
+ {
+ if (key.Trim().ToLower().StartsWith(keyToRemove.Trim().ToLower(), StringComparison.Ordinal))
+ {
+ removeKey = true;
+ break;
+ }
+ }
+ if (!removeKey)
+ {
+ res += key + ";";
+ }
+ }
+ }
+ }
+ return res;
+ }
+
+ public static string? FetchKeyInConnStr(string connStr, string[] keys)
+ {
+ // tokenize connection string and find matching key
+ if (connStr != null && keys != null)
+ {
+ string[] connProps = connStr.Split(';');
+ foreach (string cp in connProps)
+ {
+ if (!string.IsNullOrEmpty(cp.Trim()))
+ {
+ foreach (var key in keys)
+ {
+ if (cp.Trim().ToLower().StartsWith(key.Trim().ToLower(), StringComparison.Ordinal))
+ {
+ return cp.Substring(cp.IndexOf('=') + 1);
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ public static string RetrieveValueFromConnStr(string connStr, string[] keywords)
+ {
+ // tokenize connection string and retrieve value for a specific key.
+ string res = "";
+ if (connStr != null && keywords != null)
+ {
+ string[] keys = connStr.Split(';');
+ foreach (var key in keys)
+ {
+ foreach (var keyword in keywords)
+ {
+ if (!string.IsNullOrEmpty(key.Trim()))
+ {
+ if (key.Trim().ToLower().StartsWith(keyword.Trim().ToLower(), StringComparison.Ordinal))
+ {
+ res = key.Substring(key.IndexOf('=') + 1).Trim();
+ break;
+ }
+ }
+ }
+ }
+ }
+ return res;
+ }
+
+ #endregion
+}
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Azure.Test.csproj b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Azure.Test.csproj
index ad94e9b4b1..80fb2b8b5a 100644
--- a/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Azure.Test.csproj
+++ b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Azure.Test.csproj
@@ -18,10 +18,50 @@
+
+
+
+
+ PreserveNewest
+ xunit.runner.json
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Config.cs b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Config.cs
new file mode 100644
index 0000000000..2e12cc9941
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Config.cs
@@ -0,0 +1,240 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.InteropServices;
+using System.Text.Json;
+
+using Microsoft.Data.SqlClient.TestUtilities;
+
+namespace Microsoft.Data.SqlClient.Extensions.Azure.Test;
+
+// This class reads configuration information from environment variables and the
+// config.json file for use by our tests.
+//
+// Environment variables take precedence over config.json settings.
+//
+// The following variables are supported:
+//
+// ADO_POOL:
+// When defined, indicates that tests are running in an ADO-CI pool.
+//
+// SYSTEM_ACCESSTOKEN:
+// The Azure Pipelines $(System.AccessToken) to use for workload identity
+// federation.
+//
+// TEST_DEBUG_EMIT:
+// When defined, enables debug output of configuration values.
+//
+// TEST_MDS_CONFIG:
+// The path to the config file to use instead of the default. If not
+// supplied, the config file is assumed to be located next to the test
+// assembly and is named config.json.
+//
+internal static class Config
+{
+ # region Config Properties
+
+ internal static bool AdoPool { get; } = false;
+ internal static bool DebugEmit { get; } = false;
+ internal static bool IntegratedSecuritySupported { get; } = false;
+ internal static bool ManagedIdentitySupported { get; } = false;
+ internal static string PasswordConnectionString { get; } = string.Empty;
+ internal static string ServicePrincipalId { get; } = string.Empty;
+ internal static string ServicePrincipalSecret { get; } = string.Empty;
+ internal static string SystemAccessToken { get; } = string.Empty;
+ internal static bool SystemAssignedManagedIdentitySupported { get; } = false;
+ internal static string TcpConnectionString { get; } = string.Empty;
+ internal static string TenantId { get; } = string.Empty;
+ internal static bool UseManagedSniOnWindows { get; } = false;
+ internal static string UserManagedIdentityClientId { get; } = string.Empty;
+ internal static string WorkloadIdentityFederationServiceConnectionId { get; } = string.Empty;
+
+ #endregion
+
+ #region Conditional Fact/Theory Helpers
+
+ internal static bool HasIntegratedSecurityConnectionString() =>
+ !TcpConnectionString.Empty() && IntegratedSecuritySupported;
+ internal static bool HasPasswordConnectionString() => !PasswordConnectionString.Empty();
+ internal static bool HasServicePrincipal() => !ServicePrincipalId.Empty() && !ServicePrincipalSecret.Empty();
+ internal static bool HasSystemAccessToken() => !SystemAccessToken.Empty();
+ internal static bool HasTcpConnectionString() => !TcpConnectionString.Empty();
+ internal static bool HasTenantId() => !TenantId.Empty();
+ internal static bool HasUserManagedIdentityClientId() => !UserManagedIdentityClientId.Empty();
+ internal static bool HasWorkloadIdentityFederationServiceConnectionId() => !WorkloadIdentityFederationServiceConnectionId.Empty();
+
+ internal static bool SupportsIntegratedSecurity() => IntegratedSecuritySupported;
+ internal static bool SupportsManagedIdentity() => ManagedIdentitySupported;
+ internal static bool SupportsSystemAssignedManagedIdentity() => SystemAssignedManagedIdentitySupported;
+
+ internal static bool IsAzureSqlServer() =>
+ Utils.IsAzureSqlServer(new SqlConnectionStringBuilder(TcpConnectionString).DataSource);
+
+ internal static bool OnAdoPool() => AdoPool;
+ internal static bool OnLinux() => RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
+ internal static bool OnMacOS() => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
+ internal static bool OnWindows() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
+ internal static bool OnUnix() => OnLinux() || OnMacOS();
+
+ #endregion
+
+ #region Static Construction
+
+ static Config()
+ {
+ // Read from the config.json file.
+ string configPath = GetEnvVar("TEST_MDS_CONFIG");
+ if (configPath.Empty())
+ {
+ configPath = "config.json";
+ }
+
+ try
+ {
+ using JsonDocument doc =
+ JsonDocument.Parse(
+ File.ReadAllText(configPath),
+ new JsonDocumentOptions
+ {
+ CommentHandling = JsonCommentHandling.Skip,
+ AllowTrailingCommas = true
+ });
+
+ JsonElement root = doc.RootElement;
+ // See the sample config file for information about these settings:
+ //
+ // src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json
+ //
+ // The sample file is copied to the build output directory as
+ // config.json by the TestUtilities project file.
+ //
+ IntegratedSecuritySupported = GetBool(root, "SupportsIntegratedSecurity");
+ ManagedIdentitySupported = GetBool(root, "ManagedIdentitySupported");
+ PasswordConnectionString = GetString(root, "AADPasswordConnectionString");
+ ServicePrincipalId = GetString(root, "AADServicePrincipalId");
+ ServicePrincipalSecret = GetString(root, "AADServicePrincipalSecret");
+ SystemAssignedManagedIdentitySupported =
+ GetBool(root, "SupportsSystemAssignedManagedIdentity");
+ TcpConnectionString = GetString(root, "TCPConnectionString");
+ TenantId = GetString(root, "AzureKeyVaultTenantId");
+ UseManagedSniOnWindows = GetBool(root, "UseManagedSNIOnWindows");
+ UserManagedIdentityClientId = GetString(root, "UserManagedIdentityClientId");
+ WorkloadIdentityFederationServiceConnectionId =
+ GetString(root, "WorkloadIdentityFederationServiceConnectionId");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine(
+ $"Config: Failed to read config file={configPath}: {ex}");
+ }
+
+ // Apply environment variable overrides.
+ //
+ // Note that environment variables are case-sensitive on non-Windows
+ // platforms.
+ AdoPool = GetEnvFlag("ADO_POOL");
+ DebugEmit = GetEnvFlag("TEST_DEBUG_EMIT");
+ SystemAccessToken = GetEnvVar("SYSTEM_ACCESSTOKEN");
+
+ // Emit debug information if requested.
+ if (DebugEmit)
+ {
+ Console.WriteLine("Config:");
+ Console.WriteLine(
+ $" DebugEmit: {DebugEmit}");
+ Console.WriteLine(
+ $" IntegratedSecuritySupported: {IntegratedSecuritySupported}");
+ Console.WriteLine(
+ $" ManagedIdentitySupported: {ManagedIdentitySupported}");
+ Console.WriteLine(
+ $" PasswordConnectionString: {PasswordConnectionString}");
+ Console.WriteLine(
+ $" ServicePrincipalId: {ServicePrincipalId}");
+ Console.WriteLine(
+ $" ServicePrincipalSecret: {ServicePrincipalSecret.Length}");
+ Console.WriteLine(
+ $" SystemAccessToken: {SystemAccessToken}");
+ Console.WriteLine(
+ $" SystemAssignedManagedIdentitySupported: {SystemAssignedManagedIdentitySupported}");
+ Console.WriteLine(
+ $" TcpConnectionString: {TcpConnectionString}");
+ Console.WriteLine(
+ $" TenantId: {TenantId}");
+ Console.WriteLine(
+ $" UseManagedSniOnWindows: {UseManagedSniOnWindows}");
+ Console.WriteLine(
+ $" UserManagedIdentityClientId: {UserManagedIdentityClientId}");
+ Console.WriteLine(
+ " WorkloadIdentityFederationServiceConnectionId: " +
+ WorkloadIdentityFederationServiceConnectionId);
+ }
+
+ // Apply the SNI flag, if necessary. This must occur before any MDS
+ // APIs are used.
+ if (UseManagedSniOnWindows)
+ {
+ AppContext.SetSwitch(
+ "Switch.Microsoft.Data.SqlClient.UseManagedNetworkingOnWindows",
+ true);
+ }
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ private static string GetString(JsonElement element, string name)
+ {
+ if (element.TryGetProperty(name, out var property))
+ {
+ try
+ {
+ var value = property.GetString();
+ if (value is not null)
+ {
+ return value;
+ }
+ }
+ catch (InvalidOperationException)
+ {
+ // Ignore invalid values.
+ }
+ }
+
+ return string.Empty;
+ }
+ private static bool GetBool(JsonElement element, string name)
+ {
+ if (element.TryGetProperty(name, out var property))
+ {
+ try
+ {
+ return property.GetBoolean();
+ }
+ catch (InvalidOperationException)
+ {
+ // Ignore invalid values.
+ }
+ }
+
+ return false;
+ }
+
+ private static bool GetEnvFlag(string name)
+ {
+ return Environment.GetEnvironmentVariable(name) is not null;
+ }
+
+ private static string GetEnvVar(string name)
+ {
+ string? value = Environment.GetEnvironmentVariable(name);
+ if (string.IsNullOrEmpty(value))
+ {
+ return string.Empty;
+ }
+ return value;
+ }
+
+ #endregion
+}
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Azure/test/DefaultAuthProviderTests.cs b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/DefaultAuthProviderTests.cs
new file mode 100644
index 0000000000..18c09e4aa1
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/DefaultAuthProviderTests.cs
@@ -0,0 +1,60 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace Microsoft.Data.SqlClient.Extensions.Azure.Test;
+
+public class DefaultAuthProviderTests
+{
+ // Verify that our auth provider has been installed for all AAD/Entra
+ // authentication methods, and not for any other methods.
+ //
+ // Note that this isn't testing anything in the Azure package. It actually
+ // tests the static constructor of SqlAuthenticationProviderManager class in
+ // the MDS package and the static GetProvider() and SetProvider() methods of
+ // the SqlAuthenticationProvider class in the Abstractions package.
+ [Fact]
+ public void AuthProviderInstalled()
+ {
+ // Iterate over all authentication methods rather than specifying them
+ // via Theory data so that we detect any new methods that don't meet
+ // our expectations.
+ foreach (var method in
+ #if NET
+ Enum.GetValues()
+ #else
+ Enum.GetValues(typeof(SqlAuthenticationMethod)).Cast()
+ #endif
+ )
+ {
+ SqlAuthenticationProvider? provider =
+ SqlAuthenticationProvider.GetProvider(method);
+
+ switch (method)
+ {
+ #pragma warning disable 0618 // Type or member is obsolete
+ case SqlAuthenticationMethod.ActiveDirectoryPassword:
+ #pragma warning restore 0618 // Type or member is obsolete
+ case SqlAuthenticationMethod.ActiveDirectoryIntegrated:
+ case SqlAuthenticationMethod.ActiveDirectoryInteractive:
+ case SqlAuthenticationMethod.ActiveDirectoryServicePrincipal:
+ case SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow:
+ case SqlAuthenticationMethod.ActiveDirectoryManagedIdentity:
+ case SqlAuthenticationMethod.ActiveDirectoryMSI:
+ case SqlAuthenticationMethod.ActiveDirectoryDefault:
+ case SqlAuthenticationMethod.ActiveDirectoryWorkloadIdentity:
+ Assert.NotNull(provider);
+ Assert.IsType(provider);
+ break;
+
+ default:
+ // There is either no provider installed, or it is not ours.
+ if (provider is not null)
+ {
+ Assert.IsNotType(provider);
+ }
+ break;
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Azure/test/StringExtensions.cs b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/StringExtensions.cs
new file mode 100644
index 0000000000..acc7f10310
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/StringExtensions.cs
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// Adds the missing Empty() method to string that doesn't waste time on null
+// checks like String.IsNullOrEmpty() does, and has a nice short name.
+internal static class StringExtensions
+{
+ internal static bool Empty(this string str)
+ {
+ return str.Length == 0;
+ }
+}
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Azure/test/WorkloadIdentityFederationTests.cs b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/WorkloadIdentityFederationTests.cs
new file mode 100644
index 0000000000..9f63f80e2f
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/WorkloadIdentityFederationTests.cs
@@ -0,0 +1,65 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Azure.Identity;
+
+namespace Microsoft.Data.SqlClient.Extensions.Azure.Test;
+
+// Verify that we're running in an environment that supports Azure Pipelines
+// Workload Identity Federation authentication.
+public class WorkloadIdentityFederationTests
+{
+ [ConditionalFact(
+ typeof(Config),
+ nameof(Config.HasSystemAccessToken),
+ nameof(Config.HasTenantId),
+ nameof(Config.HasUserManagedIdentityClientId),
+ nameof(Config.HasWorkloadIdentityFederationServiceConnectionId))]
+ public async void GetCredential()
+ {
+ AzurePipelinesCredential credential = new(
+ // The tenant ID if the managed identity associated to our workload
+ // identity federation service connection. See:
+ //
+ // https://ms.portal.azure.com/#@microsoft.onmicrosoft.com/resource/subscriptions/654fffd0-d02d-4894-b1b7-e2dfbc44a665/resourceGroups/aad-testlab-dl797892652000/providers/Microsoft.ManagedIdentity/userAssignedIdentities/dotnetMSI/properties
+ //
+ // Note that we need a service connection configured in each Azure DevOps project
+ // (Public and ADO.Net) that uses this tenant ID.
+ //
+ Config.TenantId,
+
+ // The client ID of the managed identity associated to our workload
+ // identity federation service connection. See:
+ //
+ // https://ms.portal.azure.com/#@microsoft.onmicrosoft.com/resource/subscriptions/654fffd0-d02d-4894-b1b7-e2dfbc44a665/resourceGroups/aad-testlab-dl797892652000/providers/Microsoft.ManagedIdentity/userAssignedIdentities/dotnetMSI/overview
+ //
+ Config.UserManagedIdentityClientId,
+
+ // The Azure Dev Ops service connection ID (resourceId found in the
+ // URL) of our workload identity federation setup. See:
+ //
+ // Note that we need a service connection configured in each Azure
+ // DevOps project (Public and ADO.Net).
+ //
+ // Public project:
+ //
+ // https://sqlclientdrivers.visualstudio.com/public/_settings/adminservices?resourceId=ec9623b2-829c-497f-ae1f-7461766f9a9c
+ //
+ // ADO.Net project:
+ //
+ // https://sqlclientdrivers.visualstudio.com/ADO.Net/_settings/adminservices?resourceId=c29947a8-df6a-4ceb-b2d4-1676c57c37b9
+ //
+ Config.WorkloadIdentityFederationServiceConnectionId,
+
+ // The system access token provided by Azure Pipelines.
+ Config.SystemAccessToken);
+
+ // Acquire a token suitable for accessing Azure SQL databases.
+ var token = await credential.GetTokenAsync(
+ new(["https://database.windows.net/.default"]),
+ CancellationToken.None);
+
+ Assert.NotEmpty(token.Token);
+ }
+}
diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj
index 52812d835b..5fb084943d 100644
--- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj
@@ -1,14 +1,22 @@
- false
+
+ Microsoft.Data.SqlClient
+ $(AssemblyName).NetCore
+
net8.0;net9.0;netstandard2.0
+
+ false
$(ObjFolder)$(Configuration)\$(AssemblyName)\ref\
netcoreapp
$(BinFolder)$(Configuration)\$(AssemblyName)\ref\
$(BinFolder)$(Configuration).$(Platform)\$(AssemblyName)\netstandard\
- $(OutputPath)\$(TargetFramework)\Microsoft.Data.SqlClient.xml
+ $(OutputPath)\$(TargetFramework)\$(AssemblyName).xml
Core $(BaseProduct)
Debug;Release;
AnyCPU;x64;x86
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
index 91c8dce015..143647ee2e 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
@@ -1,7 +1,14 @@
+
Microsoft.Data.SqlClient
+ $(AssemblyName).NetCore
+
net8.0;net9.0
+
Microsoft.Data.SqlClient is not supported on this platform.
$(OS)
true
@@ -13,7 +20,7 @@
AnyCPU;x64;x86
$(ObjFolder)$(Configuration).$(Platform)\$(AssemblyName)\netcore\
$(BinFolder)$(Configuration).$(Platform)\$(AssemblyName)\netcore\
- $(OutputPath)\$(TargetFramework)\Microsoft.Data.SqlClient.xml
+ $(OutputPath)\$(TargetFramework)\$(AssemblyName).xml
true
Core $(BaseProduct)
true
diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj
index e8131c3022..765059c76f 100644
--- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj
@@ -1,10 +1,18 @@
- false
+
+ Microsoft.Data.SqlClient
+ $(AssemblyName).NetFx
+
net462
+
+ false
$(ObjFolder)$(Configuration)\$(AssemblyName)\ref\
$(BinFolder)$(Configuration)\$(AssemblyName)\ref\
- $(OutputPath)\Microsoft.Data.SqlClient.xml
+ $(OutputPath)\$(AssemblyName).xml
Framework $(BaseProduct)
Debug;Release
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
index c1775fc530..b340993a7c 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
@@ -1,15 +1,22 @@
+
+ Microsoft.Data.SqlClient
+ $(AssemblyName).NetFx
+
+ net462
+
{407890AC-9876-4FEF-A6F1-F36A876BAADE}
- net462
true
- Microsoft.Data.SqlClient
AnyCPU
$(BinFolder)$(Configuration).$(OutputPlatform)\
$(ObjFolder)$(Configuration).$(OutputPlatform)\
$(BinPath)$(AssemblyName)\netfx\
- $(OutputPath)\Microsoft.Data.SqlClient.xml
+ $(OutputPath)\$(AssemblyName).xml
$(ObjPath)$(AssemblyName)\netfx\
Framework $(BaseProduct)
false
diff --git a/src/Microsoft.Data.SqlClient/tests/Common/Common.csproj b/src/Microsoft.Data.SqlClient/tests/Common/Common.csproj
index 4133622d20..001a7410a9 100644
--- a/src/Microsoft.Data.SqlClient/tests/Common/Common.csproj
+++ b/src/Microsoft.Data.SqlClient/tests/Common/Common.csproj
@@ -34,10 +34,10 @@
-
+
PreserveNewest
xunit.runner.json
-
+
@@ -45,12 +45,11 @@
-
- PreserveNewest
- %(Filename)%(Extension)
-
+
+
+
diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AADAuthenticationTests.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AADAuthenticationTests.cs
index 32050a9716..d86b6684d1 100644
--- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AADAuthenticationTests.cs
+++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AADAuthenticationTests.cs
@@ -68,34 +68,10 @@ public async Task IsDummySqlAuthenticationProviderSetByDefault()
var provider = SqlAuthenticationProvider.GetProvider(SqlAuthenticationMethod.ActiveDirectoryInteractive);
Assert.NotNull(provider);
- Assert.Equal(typeof(DummySqlAuthenticationProvider), provider.GetType());
+ Assert.IsType(provider);
var token = await provider.AcquireTokenAsync(null);
Assert.Equal(token.AccessToken, DummySqlAuthenticationProvider.DUMMY_TOKEN_STR);
}
-
- [Fact]
- public void CustomActiveDirectoryProviderTest()
- {
- SqlAuthenticationProvider authProvider = new ActiveDirectoryAuthenticationProvider(static (result) => Task.CompletedTask);
- SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow, authProvider);
- Assert.Same(authProvider, SqlAuthenticationProvider.GetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow));
- }
-
- [Fact]
- public void CustomActiveDirectoryProviderTest_AppClientId()
- {
- SqlAuthenticationProvider authProvider = new ActiveDirectoryAuthenticationProvider(Guid.NewGuid().ToString());
- SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow, authProvider);
- Assert.Same(authProvider, SqlAuthenticationProvider.GetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow));
- }
-
- [Fact]
- public void CustomActiveDirectoryProviderTest_AppClientId_DeviceFlowCallback()
- {
- SqlAuthenticationProvider authProvider = new ActiveDirectoryAuthenticationProvider(static (result) => Task.CompletedTask, Guid.NewGuid().ToString());
- SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow, authProvider);
- Assert.Same(authProvider, SqlAuthenticationProvider.GetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow));
- }
}
}
diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.FunctionalTests.csproj b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.FunctionalTests.csproj
index 7c54891ff4..2766a8a243 100644
--- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.FunctionalTests.csproj
+++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.FunctionalTests.csproj
@@ -30,7 +30,6 @@
-
@@ -144,27 +143,22 @@
-
-
+
+
+
-
- PreserveNewest
- %(Filename)%(Extension)
-
-
-
-
+
PreserveNewest
xunit.runner.json
-
+
diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlAuthenticationProviderTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlAuthenticationProviderTest.cs
deleted file mode 100644
index c0315ce2f0..0000000000
--- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlAuthenticationProviderTest.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using Microsoft.Data.SqlClient.FunctionalTests.DataCommon;
-using Xunit;
-
-namespace Microsoft.Data.SqlClient.Tests
-{
- public class SqlAuthenticationProviderTest
- {
- [Theory]
- [InlineData(SqlAuthenticationMethod.ActiveDirectoryIntegrated)]
- #pragma warning disable 0618 // Type or member is obsolete
- [InlineData(SqlAuthenticationMethod.ActiveDirectoryPassword)]
- #pragma warning restore 0618 // Type or member is obsolete
- [InlineData(SqlAuthenticationMethod.ActiveDirectoryServicePrincipal)]
- [InlineData(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow)]
- [InlineData(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity)]
- [InlineData(SqlAuthenticationMethod.ActiveDirectoryMSI)]
- [InlineData(SqlAuthenticationMethod.ActiveDirectoryDefault)]
- [InlineData(SqlAuthenticationMethod.ActiveDirectoryWorkloadIdentity)]
- public void DefaultAuthenticationProviders(SqlAuthenticationMethod method)
- {
- Assert.IsType(SqlAuthenticationProvider.GetProvider(method));
- }
-
- // Overridden by app.config in this project
- [ConditionalTheory(typeof(TestUtility), nameof(TestUtility.IsNetFramework))]
- [InlineData(SqlAuthenticationMethod.ActiveDirectoryInteractive)]
- public void DefaultAuthenticationProviders_Interactive(SqlAuthenticationMethod method)
- {
- Assert.IsType(SqlAuthenticationProvider.GetProvider(method));
- }
- }
-}
diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs
index 916b9ac04d..3004065fba 100644
--- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs
@@ -91,7 +91,7 @@ public static class DataTestUtility
//SQL Server EngineEdition
private static string s_sqlServerEngineEdition;
-
+
// Azure Synapse EngineEditionId == 6
// More could be read at https://learn.microsoft.com/en-us/sql/t-sql/functions/serverproperty-transact-sql?view=sql-server-ver16#propertyname
public static bool IsAzureSynapse
@@ -224,6 +224,15 @@ static DataTestUtility()
AEConnStringsSetup.Add(TCPConnectionString);
}
}
+
+ // Many of our tests require a Managed Identity provider to be
+ // registered.
+ //
+ // TODO: Figure out which ones and install on-demand rather than
+ // globally.
+ SqlAuthenticationProvider.SetProvider(
+ SqlAuthenticationMethod.ActiveDirectoryManagedIdentity,
+ new ManagedIdentityProvider());
}
public static IEnumerable ConnectionStrings => GetConnectionStrings(withEnclave: true);
diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/ManagedIdentityProvider.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/ManagedIdentityProvider.cs
new file mode 100644
index 0000000000..0e590be0f9
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/ManagedIdentityProvider.cs
@@ -0,0 +1,97 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Concurrent;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Azure.Core;
+using Azure.Identity;
+
+#nullable enable
+
+namespace Microsoft.Data.SqlClient.ManualTesting.Tests;
+
+internal class ManagedIdentityProvider : SqlAuthenticationProvider
+{
+ // Our cache of managed identity user Ids to credential instances.
+ private readonly ConcurrentDictionary
+ _credentialCache = new();
+
+ // The default suffix to apply to resource scopes.
+ private const string s_defaultScopeSuffix = "/.default";
+
+ // Acquire a token using Managed Identity. The UserId in the parameters is
+ // used as the managed identity client ID. Tokens are cached per UserId.
+ //
+ // GOTCHA: This assumes that the Resource and Authority in the parameters
+ // never change for a given UserId, which is probably a safe assumption for
+ // tests.
+ //
+ public override async Task AcquireTokenAsync(
+ SqlAuthenticationParameters parameters)
+ {
+ if (parameters.UserId is null)
+ {
+ throw new TokenException(
+ "Refusing to acquire token for ManagedIdentity with null UserId");
+ }
+
+ try
+ {
+ // Build an appropriate scope.
+ string scope = parameters.Resource.EndsWith(
+ s_defaultScopeSuffix, StringComparison.Ordinal)
+ ? parameters.Resource
+ : parameters.Resource + s_defaultScopeSuffix;
+
+ TokenRequestContext context = new([scope]);
+
+ TokenCredentialOptions options = new()
+ {
+ AuthorityHost = new Uri(parameters.Authority)
+ };
+
+ // Create or re-use the ManagedIdentityCredential for this UserId.
+ ManagedIdentityCredential credential =
+ _credentialCache.GetOrAdd(
+ parameters.UserId,
+ (_) => new(parameters.UserId, options));
+
+ // Set up a cancellation token based on the authentication timeout,
+ // ignoring overflow since this is just test code.
+ using CancellationTokenSource cancellor = new();
+ cancellor.CancelAfter(parameters.ConnectionTimeout * 1000);
+
+ // Acquire the token, which may be cached by the credential.
+ AccessToken token =
+ await credential.GetTokenAsync(context, cancellor.Token)
+ .ConfigureAwait(false);
+
+ return new(token.Token, token.ExpiresOn);
+ }
+ catch (Exception ex)
+ {
+ throw new TokenException(
+ $"Failed to acquire token for ManagedIdentity " +
+ $"userId ={parameters.UserId} error={ex.Message}", ex);
+ }
+ }
+
+ /// We support only the Managed Identity authentication method.
+ public override bool IsSupported(SqlAuthenticationMethod authenticationMethod)
+ {
+ return authenticationMethod == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity;
+ }
+
+ // The exception we throw on any errors acquiring tokens.
+ private sealed class TokenException : SqlAuthenticationProviderException
+ {
+ internal TokenException(string message, Exception? causedBy = null)
+ : base(message, causedBy)
+ {
+ }
+ }
+}
diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/UsernamePasswordProvider.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/UsernamePasswordProvider.cs
new file mode 100644
index 0000000000..9c4ca9828e
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/UsernamePasswordProvider.cs
@@ -0,0 +1,75 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Security;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Identity.Client;
+
+#nullable enable
+
+namespace Microsoft.Data.SqlClient.ManualTesting.Tests;
+
+internal class UsernamePasswordProvider : SqlAuthenticationProvider
+{
+ string _appClientId;
+ const string s_defaultScopeSuffix = "/.default";
+
+ internal UsernamePasswordProvider(string appClientId)
+ {
+ _appClientId = appClientId;
+ }
+
+ public override async Task AcquireTokenAsync(SqlAuthenticationParameters parameters)
+ {
+ try
+ {
+ string scope =
+ parameters.Resource.EndsWith(s_defaultScopeSuffix, StringComparison.Ordinal)
+ ? parameters.Resource
+ : parameters.Resource + s_defaultScopeSuffix;
+
+ var cts = new CancellationTokenSource();
+ cts.CancelAfter(parameters.ConnectionTimeout * 1000);
+
+ string[] scopes = new string[] { scope };
+ SecureString password = new SecureString();
+
+ AuthenticationResult result =
+ #pragma warning disable CS0618 // Type or member is obsolete
+ await PublicClientApplicationBuilder.Create(_appClientId)
+ .WithAuthority(parameters.Authority)
+ .Build()
+ .AcquireTokenByUsernamePassword(scopes, parameters.UserId, parameters.Password)
+ #pragma warning restore CS0618 // Type or member is obsolete
+ .WithCorrelationId(parameters.ConnectionId)
+ .ExecuteAsync(cancellationToken: cts.Token);
+
+ return new SqlAuthenticationToken(result.AccessToken, result.ExpiresOn);
+ }
+ catch (Exception ex)
+ {
+ throw new TokenException(
+ $"Failed to acquire token for ManagedIdentity " +
+ $"userId ={parameters.UserId} error={ex.Message}", ex);
+ }
+ }
+
+ public override bool IsSupported(SqlAuthenticationMethod authenticationMethod)
+ {
+ #pragma warning disable 0618 // Type or member is obsolete
+ return authenticationMethod.Equals(SqlAuthenticationMethod.ActiveDirectoryPassword);
+ #pragma warning restore 0618 // Type or member is obsolete
+ }
+
+ // The exception we throw on any errors acquiring tokens.
+ private sealed class TokenException : SqlAuthenticationProviderException
+ {
+ internal TokenException(string message, Exception? causedBy = null)
+ : base(message, causedBy)
+ {
+ }
+ }
+}
diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj
index 697529f6f7..da19139ff3 100644
--- a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj
@@ -272,9 +272,11 @@
+
+
@@ -329,14 +331,12 @@
-
-
@@ -372,11 +372,11 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
- PreserveNewest
- %(Filename)%(Extension)
-
+
+
+
+
PreserveNewest
@@ -390,9 +390,9 @@
Always
-
+
PreserveNewest
xunit.runner.json
-
+
diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs
index 027acfde23..e99192732d 100644
--- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs
@@ -2,7 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using System;
+using System;
using System.Diagnostics;
using Microsoft.Data.SqlClient.ManualTesting.Tests.SQL.Common.SystemDataInternals;
using Xunit;
@@ -22,10 +22,19 @@ public AADFedAuthTokenRefreshTest(ITestOutputHelper testOutputHelper)
[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAADPasswordConnStrSetup))]
public void FedAuthTokenRefreshTest()
{
- string connectionString = DataTestUtility.AADPasswordConnectionString;
+ #pragma warning disable 0618 // Type or member is obsolete
+ SqlAuthenticationProvider original = SqlAuthenticationProvider.GetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword);
+ #pragma warning restore 0618 // Type or member is obsolete
- using (SqlConnection connection = new SqlConnection(connectionString))
+ try
{
+ #pragma warning disable 0618 // Type or member is obsolete
+ SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, new UsernamePasswordProvider(DataTestUtility.ApplicationClientId));
+ #pragma warning restore 0618 // Type or member is obsolete
+
+ string connectionString = DataTestUtility.AADPasswordConnectionString;
+
+ using SqlConnection connection = new SqlConnection(connectionString);
connection.Open();
string oldTokenHash = "";
@@ -65,6 +74,16 @@ public void FedAuthTokenRefreshTest()
Assert.True(newLocalExpiryTime > oldLocalExpiryTime, "The refreshed token must have a new or later expiry time.");
}
}
+ finally
+ {
+ if (original is not null)
+ {
+ // Reset to driver internal provider.
+ #pragma warning disable 0618 // Type or member is obsolete
+ SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, original);
+ #pragma warning restore 0618 // Type or member is obsolete
+ }
+ }
}
[Conditional("DEBUG")]
diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs
index 2c46c2598d..f2c0128c46 100644
--- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs
@@ -5,59 +5,14 @@
using System;
using System.Diagnostics;
using System.Security;
-using System.Threading;
using System.Threading.Tasks;
using Azure.Core;
-using Microsoft.Identity.Client;
using Xunit;
namespace Microsoft.Data.SqlClient.ManualTesting.Tests
{
- public class AADConnectionsTest
+ public class AADConnectionTest
{
- class CustomSqlAuthenticationProvider : SqlAuthenticationProvider
- {
- string _appClientId;
-
- internal CustomSqlAuthenticationProvider(string appClientId)
- {
- _appClientId = appClientId;
- }
-
- public override async Task AcquireTokenAsync(SqlAuthenticationParameters parameters)
- {
- string s_defaultScopeSuffix = "/.default";
- string scope = parameters.Resource.EndsWith(s_defaultScopeSuffix, StringComparison.Ordinal) ? parameters.Resource : parameters.Resource + s_defaultScopeSuffix;
-
- _ = parameters.ServerName;
- _ = parameters.DatabaseName;
- _ = parameters.ConnectionId;
-
- var cts = new CancellationTokenSource();
- cts.CancelAfter(parameters.ConnectionTimeout * 1000);
-
- string[] scopes = new string[] { scope };
- SecureString password = new SecureString();
-
-#pragma warning disable CS0618 // Type or member is obsolete
- AuthenticationResult result = await PublicClientApplicationBuilder.Create(_appClientId)
- .WithAuthority(parameters.Authority)
- .Build().AcquireTokenByUsernamePassword(scopes, parameters.UserId, parameters.Password)
- .WithCorrelationId(parameters.ConnectionId)
- .ExecuteAsync(cancellationToken: cts.Token);
-#pragma warning restore CS0618 // Type or member is obsolete
-
- return new SqlAuthenticationToken(result.AccessToken, result.ExpiresOn);
- }
-
- public override bool IsSupported(SqlAuthenticationMethod authenticationMethod)
- {
- #pragma warning disable 0618 // Type or member is obsolete
- return authenticationMethod.Equals(SqlAuthenticationMethod.ActiveDirectoryPassword);
- #pragma warning restore 0618 // Type or member is obsolete
- }
- }
-
private static void ConnectAndDisconnect(string connectionString, SqlCredential credential = null)
{
using (SqlConnection conn = new SqlConnection(connectionString))
@@ -79,15 +34,6 @@ private static void ConnectAndDisconnect(string connectionString, SqlCredential
private static bool IsManagedIdentitySetup() => DataTestUtility.ManagedIdentitySupported;
private static bool SupportsSystemAssignedManagedIdentity() => DataTestUtility.SupportsSystemAssignedManagedIdentity;
- [PlatformSpecific(TestPlatforms.Windows)]
- [ConditionalFact(nameof(IsAccessTokenSetup), nameof(IsAADConnStringsSetup), nameof(IsManagedIdentitySetup))]
- public static void KustoDatabaseTest()
- {
- // This is a sample Kusto database that can be connected by any AD account.
- using SqlConnection connection = new SqlConnection($"Data Source=help.kusto.windows.net; Authentication=Active Directory Default;Trust Server Certificate=True;User ID = {DataTestUtility.UserManagedIdentityClientId};");
- connection.Open();
- Assert.True(connection.State == System.Data.ConnectionState.Open);
- }
[ConditionalFact(nameof(IsAccessTokenSetup), nameof(IsAADConnStringsSetup))]
public static void AccessTokenTest()
@@ -213,47 +159,33 @@ public static void AADPasswordWithIntegratedSecurityTrue()
Assert.Contains(expectedMessage, e.Message);
}
- [ConditionalFact(nameof(IsAccessTokenSetup), nameof(IsAADConnStringsSetup))]
- public static void AADPasswordWithWrongPassword()
- {
- string[] credKeys = { "Password", "PWD" };
- string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys) + "Password=TestPassword;";
-
- Assert.Throws(() => ConnectAndDisconnect(connStr));
-
- // We cannot verify error message with certainity as driver may cache token from other tests for current user
- // and error message may change accordingly.
- }
-
[ConditionalFact(nameof(IsAADConnStringsSetup))]
public static void GetAccessTokenByPasswordTest()
{
- // Clear token cache for code coverage.
- ActiveDirectoryAuthenticationProvider.ClearUserTokenCache();
- using (SqlConnection connection = new SqlConnection(DataTestUtility.AADPasswordConnectionString))
+ #pragma warning disable 0618 // Type or member is obsolete
+ SqlAuthenticationProvider original = SqlAuthenticationProvider.GetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword);
+ #pragma warning restore 0618 // Type or member is obsolete
+
+ try
{
- connection.Open();
- Assert.True(connection.State == System.Data.ConnectionState.Open);
- }
- }
+ #pragma warning disable 0618 // Type or member is obsolete
+ SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, new UsernamePasswordProvider(DataTestUtility.ApplicationClientId));
+ #pragma warning restore 0618 // Type or member is obsolete
- [ConditionalFact(nameof(IsAADConnStringsSetup))]
- public static void TestADPasswordAuthentication()
- {
- // Connect to Azure DB with password and retrieve user name.
- using (SqlConnection conn = new SqlConnection(DataTestUtility.AADPasswordConnectionString))
+ using (SqlConnection connection = new SqlConnection(DataTestUtility.AADPasswordConnectionString))
+ {
+ connection.Open();
+ Assert.True(connection.State == System.Data.ConnectionState.Open);
+ }
+ }
+ finally
{
- conn.Open();
- using (SqlCommand sqlCommand = new SqlCommand
- (
- cmdText: $"SELECT SUSER_SNAME();",
- connection: conn,
- transaction: null
- ))
+ if (original is not null)
{
- string customerId = (string)sqlCommand.ExecuteScalar();
- string expected = DataTestUtility.RetrieveValueFromConnStr(DataTestUtility.AADPasswordConnectionString, new string[] { "User ID", "UID" });
- Assert.Equal(expected, customerId);
+ // Reset to driver internal provider.
+ #pragma warning disable 0618 // Type or member is obsolete
+ SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, original);
+ #pragma warning restore 0618 // Type or member is obsolete
}
}
}
@@ -262,28 +194,41 @@ public static void TestADPasswordAuthentication()
public static void TestCustomProviderAuthentication()
{
#pragma warning disable 0618 // Type or member is obsolete
- SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, new CustomSqlAuthenticationProvider(DataTestUtility.ApplicationClientId));
+ SqlAuthenticationProvider original = SqlAuthenticationProvider.GetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword);
#pragma warning restore 0618 // Type or member is obsolete
- // Connect to Azure DB with password and retrieve user name using custom authentication provider
- using (SqlConnection conn = new SqlConnection(DataTestUtility.AADPasswordConnectionString))
+
+ try
{
- conn.Open();
- using (SqlCommand sqlCommand = new SqlCommand
- (
- cmdText: $"SELECT SUSER_SNAME();",
- connection: conn,
- transaction: null
- ))
+ #pragma warning disable 0618 // Type or member is obsolete
+ SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, new UsernamePasswordProvider(DataTestUtility.ApplicationClientId));
+ #pragma warning restore 0618 // Type or member is obsolete
+ // Connect to Azure DB with password and retrieve user name using custom authentication provider
+ using (SqlConnection conn = new SqlConnection(DataTestUtility.AADPasswordConnectionString))
{
- string customerId = (string)sqlCommand.ExecuteScalar();
- string expected = DataTestUtility.RetrieveValueFromConnStr(DataTestUtility.AADPasswordConnectionString, new string[] { "User ID", "UID" });
- Assert.Equal(expected, customerId);
+ conn.Open();
+ using (SqlCommand sqlCommand = new SqlCommand
+ (
+ cmdText: $"SELECT SUSER_SNAME();",
+ connection: conn,
+ transaction: null
+ ))
+ {
+ string customerId = (string)sqlCommand.ExecuteScalar();
+ string expected = DataTestUtility.RetrieveValueFromConnStr(DataTestUtility.AADPasswordConnectionString, new string[] { "User ID", "UID" });
+ Assert.Equal(expected, customerId);
+ }
+ }
+ }
+ finally
+ {
+ if (original is not null)
+ {
+ // Reset to driver internal provider.
+ #pragma warning disable 0618 // Type or member is obsolete
+ SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, original);
+ #pragma warning restore 0618 // Type or member is obsolete
}
}
- // Reset to driver internal provider.
- #pragma warning disable 0618 // Type or member is obsolete
- SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, new ActiveDirectoryAuthenticationProvider(DataTestUtility.ApplicationClientId));
- #pragma warning restore 0618 // Type or member is obsolete
}
[ConditionalFact(nameof(IsAADConnStringsSetup))]
@@ -320,92 +265,6 @@ public static void MFAAuthWithPassword()
Assert.Contains(expectedMessage, e.Message);
}
- [ConditionalFact(nameof(IsAADConnStringsSetup))]
- public static void EmptyPasswordInConnStrAADPassword()
- {
- // connection fails with expected error message.
- string[] pwdKey = { "Password", "PWD" };
- string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, pwdKey) + "Password=;";
- SqlException e = Assert.Throws(() => ConnectAndDisconnect(connStr));
-
- string user = DataTestUtility.FetchKeyInConnStr(DataTestUtility.AADPasswordConnectionString, new string[] { "User Id", "UID" });
- string expectedMessage = string.Format("Failed to authenticate the user {0} in Active Directory (Authentication=ActiveDirectoryPassword).", user);
- Assert.Contains(expectedMessage, e.Message);
- }
-
- [PlatformSpecific(TestPlatforms.Windows)]
- [ConditionalFact(nameof(IsAADConnStringsSetup))]
- public static void EmptyCredInConnStrAADPassword()
- {
- // connection fails with expected error message.
- string[] removeKeys = { "User ID", "Password", "UID", "PWD" };
- string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, removeKeys) + "User ID=; Password=;";
- SqlException e = Assert.Throws(() => ConnectAndDisconnect(connStr));
-
- string expectedMessage = "Failed to authenticate the user in Active Directory (Authentication=ActiveDirectoryPassword).";
- Assert.Contains(expectedMessage, e.Message);
- }
-
- [PlatformSpecific(TestPlatforms.AnyUnix)]
- [ConditionalFact(nameof(IsAADConnStringsSetup))]
- public static void EmptyCredInConnStrAADPasswordAnyUnix()
- {
- // connection fails with expected error message.
- string[] removeKeys = { "User ID", "Password", "UID", "PWD" };
- string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, removeKeys) + "User ID=; Password=;";
- SqlException e = Assert.Throws(() => ConnectAndDisconnect(connStr));
-
- string expectedMessage = "MSAL cannot determine the username (UPN) of the currently logged in user.For Integrated Windows Authentication and Username/Password flows, please use .WithUsername() before calling ExecuteAsync().";
- Assert.Contains(expectedMessage, e.Message);
- }
-
- [ConditionalFact(nameof(IsAADConnStringsSetup))]
- public static void AADPasswordWithInvalidUser()
- {
- // connection fails with expected error message.
- string[] removeKeys = { "User ID", "UID" };
- string user = "testdotnet@domain.com";
- string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, removeKeys) + $"User ID={user}";
- SqlException e = Assert.Throws(() => ConnectAndDisconnect(connStr));
-
- string expectedMessage = string.Format("Failed to authenticate the user {0} in Active Directory (Authentication=ActiveDirectoryPassword).", user);
- Assert.Contains(expectedMessage, e.Message);
- }
-
- [ConditionalFact(nameof(IsAADConnStringsSetup))]
- public static void NoCredentialsActiveDirectoryPassword()
- {
- // test Passes with correct connection string.
- ConnectAndDisconnect(DataTestUtility.AADPasswordConnectionString);
-
- // connection fails with expected error message.
- string[] credKeys = { "User ID", "Password", "UID", "PWD" };
- string connStrWithNoCred = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys);
- InvalidOperationException e = Assert.Throws(() => ConnectAndDisconnect(connStrWithNoCred));
-
- string expectedMessage = "Either Credential or both 'User ID' and 'Password' (or 'UID' and 'PWD') connection string keywords must be specified, if 'Authentication=Active Directory Password'.";
- Assert.Contains(expectedMessage, e.Message);
- }
-
- [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAADServicePrincipalSetup))]
- public static void NoCredentialsActiveDirectoryServicePrincipal()
- {
- // test Passes with correct connection string.
- string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD" };
- string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, removeKeys) +
- $"Authentication=Active Directory Service Principal; User ID={DataTestUtility.AADServicePrincipalId}; PWD={DataTestUtility.AADServicePrincipalSecret};";
- ConnectAndDisconnect(connStr);
-
- // connection fails with expected error message.
- string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" };
- string connStrWithNoCred = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys) +
- "Authentication=Active Directory Service Principal;";
- InvalidOperationException e = Assert.Throws(() => ConnectAndDisconnect(connStrWithNoCred));
-
- string expectedMessage = "Either Credential or both 'User ID' and 'Password' (or 'UID' and 'PWD') connection string keywords must be specified, if 'Authentication=Active Directory Service Principal'.";
- Assert.Contains(expectedMessage, e.Message);
- }
-
[ConditionalFact(nameof(IsAADConnStringsSetup))]
public static void ActiveDirectoryDeviceCodeFlowWithUserIdMustFail()
{
@@ -496,22 +355,6 @@ public static void ActiveDirectoryManagedIdentityWithPasswordMustFail()
Assert.Contains(expectedMessage, e.Message);
}
- [InlineData("2445343 2343253")]
- [InlineData("2445343$#^@@%2343253")]
- [ConditionalTheory(nameof(IsAADConnStringsSetup), nameof(IsManagedIdentitySetup))]
- public static void ActiveDirectoryManagedIdentityWithInvalidUserIdMustFail(string userId)
- {
- // connection fails with expected error message.
- string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" };
- string connStrWithNoCred = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys) +
- $"Authentication=Active Directory Managed Identity; User Id={userId}";
-
- SqlException e = Assert.Throws(() => ConnectAndDisconnect(connStrWithNoCred));
-
- string expectedMessage = "[Managed Identity] Authentication unavailable";
- Assert.Contains(expectedMessage, e.GetBaseException().Message, StringComparison.OrdinalIgnoreCase);
- }
-
[ConditionalFact(nameof(IsAADConnStringsSetup))]
public static void ActiveDirectoryMSIWithCredentialsMustFail()
{
@@ -653,85 +496,66 @@ public static void AccessTokenCallbackReceivesUsernameAndPassword()
}
}
- [ConditionalFact(nameof(IsAADConnStringsSetup), nameof(IsManagedIdentitySetup))]
- public static void ActiveDirectoryDefaultMustPass()
- {
- string[] credKeys = { "Authentication", "User ID", "Password", "UID", "PWD" };
- string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, credKeys) +
- $"Authentication=ActiveDirectoryDefault;User ID={DataTestUtility.UserManagedIdentityClientId};";
-
- // Connection should be established using Managed Identity by default.
- ConnectAndDisconnect(connStr);
- }
-
- [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsIntegratedSecuritySetup), nameof(DataTestUtility.AreConnStringsSetup))]
- public static void ADIntegratedUsingSSPI()
- {
- // test Passes with correct connection string.
- string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD", "Trusted_Connection", "Integrated Security" };
- string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.TCPConnectionString, removeKeys) +
- $"Authentication=Active Directory Integrated;";
- ConnectAndDisconnect(connStr);
- }
-
// Test passes locally everytime, but in pieplines fails randomly with uncertainity.
// e.g. Second AAD connection too slow (802ms)! (More than 30% of the first (576ms).)
[ActiveIssue("16058")]
[ConditionalFact(nameof(IsAADConnStringsSetup))]
public static void ConnectionSpeed()
{
- var connString = DataTestUtility.AADPasswordConnectionString;
+ #pragma warning disable 0618 // Type or member is obsolete
+ SqlAuthenticationProvider original = SqlAuthenticationProvider.GetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword);
+ #pragma warning restore 0618 // Type or member is obsolete
- //Ensure server endpoints are warm
- using (var connectionDrill = new SqlConnection(connString))
+ try
{
- connectionDrill.Open();
- }
+ #pragma warning disable 0618 // Type or member is obsolete
+ SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, new UsernamePasswordProvider(DataTestUtility.ApplicationClientId));
+ #pragma warning restore 0618 // Type or member is obsolete
- SqlConnection.ClearAllPools();
- ActiveDirectoryAuthenticationProvider.ClearUserTokenCache();
+ var connString = DataTestUtility.AADPasswordConnectionString;
- Stopwatch firstConnectionTime = new Stopwatch();
- Stopwatch secondConnectionTime = new Stopwatch();
+ //Ensure server endpoints are warm
+ using (var connectionDrill = new SqlConnection(connString))
+ {
+ connectionDrill.Open();
+ }
+
+ SqlConnection.ClearAllPools();
- using (var connectionDrill = new SqlConnection(connString))
+ Stopwatch firstConnectionTime = new Stopwatch();
+ Stopwatch secondConnectionTime = new Stopwatch();
+
+ using (var connectionDrill = new SqlConnection(connString))
+ {
+ firstConnectionTime.Start();
+ connectionDrill.Open();
+ firstConnectionTime.Stop();
+ using (var connectionDrill2 = new SqlConnection(connString))
+ {
+ secondConnectionTime.Start();
+ connectionDrill2.Open();
+ secondConnectionTime.Stop();
+ }
+ }
+
+ // Subsequent AAD connections within a short timeframe should use an auth token cached from the connection pool
+ // Second connection speed in tests was typically 10-15% of the first connection time. Using 30% since speeds may vary.
+ Assert.True(((double)secondConnectionTime.ElapsedMilliseconds / firstConnectionTime.ElapsedMilliseconds) < 0.30, $"Second AAD connection too slow ({secondConnectionTime.ElapsedMilliseconds}ms)! (More than 30% of the first ({firstConnectionTime.ElapsedMilliseconds}ms).)");
+ }
+ finally
{
- firstConnectionTime.Start();
- connectionDrill.Open();
- firstConnectionTime.Stop();
- using (var connectionDrill2 = new SqlConnection(connString))
+ if (original is not null)
{
- secondConnectionTime.Start();
- connectionDrill2.Open();
- secondConnectionTime.Stop();
+ // Reset to driver internal provider.
+ #pragma warning disable 0618 // Type or member is obsolete
+ SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, original);
+ #pragma warning restore 0618 // Type or member is obsolete
}
}
-
- // Subsequent AAD connections within a short timeframe should use an auth token cached from the connection pool
- // Second connection speed in tests was typically 10-15% of the first connection time. Using 30% since speeds may vary.
- Assert.True(((double)secondConnectionTime.ElapsedMilliseconds / firstConnectionTime.ElapsedMilliseconds) < 0.30, $"Second AAD connection too slow ({secondConnectionTime.ElapsedMilliseconds}ms)! (More than 30% of the first ({firstConnectionTime.ElapsedMilliseconds}ms).)");
}
#region Managed Identity Authentication tests
- [ConditionalFact(nameof(IsAADConnStringsSetup), nameof(IsManagedIdentitySetup), nameof(SupportsSystemAssignedManagedIdentity))]
- public static void SystemAssigned_ManagedIdentityTest()
- {
- string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD" };
- string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, removeKeys) +
- $"Authentication=Active Directory Managed Identity;";
- ConnectAndDisconnect(connStr);
- }
-
- [ConditionalFact(nameof(IsAADConnStringsSetup), nameof(IsManagedIdentitySetup))]
- public static void UserAssigned_ManagedIdentityTest()
- {
- string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD" };
- string connStr = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.AADPasswordConnectionString, removeKeys) +
- $"Authentication=Active Directory Managed Identity; User Id={DataTestUtility.UserManagedIdentityClientId};";
- ConnectAndDisconnect(connStr);
- }
-
[ConditionalFact(nameof(IsAADConnStringsSetup), nameof(IsManagedIdentitySetup), nameof(SupportsSystemAssignedManagedIdentity))]
public static void AccessToken_SystemManagedIdentityTest()
{
@@ -760,36 +584,6 @@ public static void AccessToken_UserManagedIdentityTest()
}
}
- [ConditionalFact(nameof(AreConnStringsSetup), nameof(IsAzure), nameof(IsManagedIdentitySetup), nameof(SupportsSystemAssignedManagedIdentity))]
- public static void Azure_SystemManagedIdentityTest()
- {
- string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD", "Trusted_Connection", "Integrated Security" };
- string connectionString = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.TCPConnectionString, removeKeys)
- + $"Authentication=Active Directory Managed Identity;";
-
- using (SqlConnection conn = new SqlConnection(connectionString))
- {
- conn.Open();
-
- Assert.True(conn.State == System.Data.ConnectionState.Open);
- }
- }
-
- [ConditionalFact(nameof(AreConnStringsSetup), nameof(IsAzure), nameof(IsManagedIdentitySetup))]
- public static void Azure_UserManagedIdentityTest()
- {
- string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD", "Trusted_Connection", "Integrated Security" };
- string connectionString = DataTestUtility.RemoveKeysInConnStr(DataTestUtility.TCPConnectionString, removeKeys)
- + $"Authentication=Active Directory Managed Identity; User Id={DataTestUtility.UserManagedIdentityClientId}";
-
- using (SqlConnection conn = new SqlConnection(connectionString))
- {
- conn.Open();
-
- Assert.True(conn.State == System.Data.ConnectionState.Open);
- }
- }
-
[ConditionalFact(nameof(AreConnStringsSetup), nameof(IsAzure), nameof(IsAccessTokenSetup), nameof(IsManagedIdentitySetup), nameof(SupportsSystemAssignedManagedIdentity))]
public static void Azure_AccessToken_SystemManagedIdentityTest()
{
diff --git a/src/Microsoft.Data.SqlClient/tests/StressTests/Directory.Build.props b/src/Microsoft.Data.SqlClient/tests/StressTests/Directory.Build.props
index 26de699227..bc5b40518d 100644
--- a/src/Microsoft.Data.SqlClient/tests/StressTests/Directory.Build.props
+++ b/src/Microsoft.Data.SqlClient/tests/StressTests/Directory.Build.props
@@ -3,6 +3,12 @@
+
+
+
diff --git a/src/Microsoft.Data.SqlClient/tests/StressTests/Directory.Packages.props b/src/Microsoft.Data.SqlClient/tests/StressTests/Directory.Packages.props
index 45b1a5018f..12b1aecc30 100644
--- a/src/Microsoft.Data.SqlClient/tests/StressTests/Directory.Packages.props
+++ b/src/Microsoft.Data.SqlClient/tests/StressTests/Directory.Packages.props
@@ -25,14 +25,31 @@
-
- 6.1.0-preview2.25178.5
+
+
+
+ 6.1.2
+
+
+
+
+
+
+
+
+ $(AzurePackageVersion)
+
+
+
+
+ 1.0.0
+
diff --git a/src/Microsoft.Data.SqlClient/tests/StressTests/SqlClient.Stress.Framework/SqlClient.Stress.Framework.csproj b/src/Microsoft.Data.SqlClient/tests/StressTests/SqlClient.Stress.Framework/SqlClient.Stress.Framework.csproj
index 91fe0f81ee..ba086b58a8 100644
--- a/src/Microsoft.Data.SqlClient/tests/StressTests/SqlClient.Stress.Framework/SqlClient.Stress.Framework.csproj
+++ b/src/Microsoft.Data.SqlClient/tests/StressTests/SqlClient.Stress.Framework/SqlClient.Stress.Framework.csproj
@@ -6,6 +6,7 @@
+
diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft.Data.SqlClient.UnitTests.csproj b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft.Data.SqlClient.UnitTests.csproj
index a105ccdf29..a60240475a 100644
--- a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft.Data.SqlClient.UnitTests.csproj
+++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft.Data.SqlClient.UnitTests.csproj
@@ -43,17 +43,15 @@
+
+
+
+
-
- PreserveNewest
- %(Filename)%(Extension)
-
-
-
-
+
PreserveNewest
xunit.runner.json
-
+
diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Microsoft.Data.SqlClient.TestUtilities.csproj b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Microsoft.Data.SqlClient.TestUtilities.csproj
index 45ba5c2cf9..2f802adc75 100644
--- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Microsoft.Data.SqlClient.TestUtilities.csproj
+++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Microsoft.Data.SqlClient.TestUtilities.csproj
@@ -1,9 +1,6 @@
- netfx
- netcoreapp
- win
- win-$(Platform)
+ netstandard2.0
$(ObjFolder)$(Configuration).$(Platform)\$(AssemblyName)
$(BinFolder)$(Configuration).$(Platform)\$(AssemblyName)
@@ -14,10 +11,6 @@
PreserveNewest
-
-
-
-
diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Utils.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Utils.cs
index 5d708e757d..bb1e2220a8 100644
--- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Utils.cs
+++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Utils.cs
@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using System;
-using System.Collections.Generic;
namespace Microsoft.Data.SqlClient.TestUtilities
{
diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json
index 5ef2e9c99e..4362d07c75 100644
--- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json
+++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json
@@ -35,5 +35,6 @@
"ManagedIdentitySupported": true,
"UserManagedIdentityClientId": "",
"PowerShellPath": "",
- "AliasName": ""
+ "AliasName": "",
+ "WorkloadIdentityFederationServiceConnectionId": ""
}
diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/xunit.runner.json b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/xunit.runner.json
index 42755c93ec..60e094a725 100644
--- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/xunit.runner.json
+++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/xunit.runner.json
@@ -1,9 +1,17 @@
{
- "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json",
- "diagnosticMessages": true,
- "parallelizeAssembly": true,
- "shadowCopy": false,
- "printMaxEnumerableLength": 0,
- "printMaxStringLength": 0,
- "showLiveOutput": false
+ "_comment": "Options for v3+ are prefixed with '_v3_'",
+
+ "$schema": "https://xunit.net/schema/v2.8/xunit.runner.schema.json",
+ "_v3_$schema": "https://xunit.net/schema/current/xunit.runner.schema.json",
+
+ "diagnosticMessages": true,
+ "parallelizeAssembly": true,
+ "shadowCopy": false,
+
+ "_v3_culture": "invariant",
+ "_v3_printMaxEnumerableLength": 0,
+ "_v3_printMaxObjectDepth": 0,
+ "_v3_printMaxObjectMemberCount": 0,
+ "_v3_printMaxStringLength": 0,
+ "_v3_showLiveOutput": true
}
diff --git a/src/Microsoft.SqlServer.Server/Microsoft.SqlServer.Server.csproj b/src/Microsoft.SqlServer.Server/Microsoft.SqlServer.Server.csproj
index 48d163cab6..89d6d91b37 100644
--- a/src/Microsoft.SqlServer.Server/Microsoft.SqlServer.Server.csproj
+++ b/src/Microsoft.SqlServer.Server/Microsoft.SqlServer.Server.csproj
@@ -6,7 +6,7 @@
net46;netstandard2.0
$(ObjFolder)$(Configuration).$(Platform)\$(AssemblyName)\
$(BinFolder)$(Configuration).$(Platform)\$(AssemblyName)\
- $(OutputPath)$(TargetFramework)\Microsoft.SqlServer.Server.xml
+ $(OutputPath)$(TargetFramework)\$(AssemblyName).xml
portable
false
true
diff --git a/src/Microsoft.SqlServer.Server/StringsHelper.cs b/src/Microsoft.SqlServer.Server/StringsHelper.cs
index 610839adbf..7f010f3a2c 100644
--- a/src/Microsoft.SqlServer.Server/StringsHelper.cs
+++ b/src/Microsoft.SqlServer.Server/StringsHelper.cs
@@ -48,14 +48,18 @@ public static string GetResourceString(string res)
{
StringsHelper sys = GetLoader();
if (sys == null)
+ {
return null;
+ }
// If "res" is a resource id, temp will not be null, "res" will contain the retrieved resource string.
// If "res" is not a resource id, temp will be null.
string temp = sys._resources.GetString(res, Culture);
if (temp != null)
+ {
res = temp;
-
+ }
+
return res;
}
public static string GetString(string res, params object[] args)
diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec
index fe8417e554..27f4db08fb 100644
--- a/tools/specs/Microsoft.Data.SqlClient.nuspec
+++ b/tools/specs/Microsoft.Data.SqlClient.nuspec
@@ -91,20 +91,20 @@
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
@@ -115,105 +115,110 @@
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
diff --git a/tools/targets/CopySniDllsForNetFxProjectReferenceBuilds.targets b/tools/targets/CopySniDllsForNetFxProjectReferenceBuilds.targets
new file mode 100644
index 0000000000..e24a40cb01
--- /dev/null
+++ b/tools/targets/CopySniDllsForNetFxProjectReferenceBuilds.targets
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+ .NetFx
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/targets/GenerateMdsPackage.targets b/tools/targets/GenerateMdsPackage.targets
index 1c90310770..1b3418860a 100644
--- a/tools/targets/GenerateMdsPackage.targets
+++ b/tools/targets/GenerateMdsPackage.targets
@@ -1,5 +1,11 @@
+
+
+ .NetFx
+ .NetCore
+
+
@@ -8,6 +14,6 @@
-
+