From d576833254a0174df108ff91b875eac347a03921 Mon Sep 17 00:00:00 2001
From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com>
Date: Tue, 30 Sep 2025 15:11:04 -0300
Subject: [PATCH 01/13] Abstractions Package - C# project changes (#3626)
---
.editorconfig | 2 +-
BUILDGUIDE.md | 81 +++--
Directory.Packages.props | 8 +
NuGet.config | 13 +-
build.proj | 161 ++++++++--
.../jobs/build-signed-package-job.yml | 1 +
.../templates/jobs/ci-build-nugets-job.yml | 2 +
.../steps/generate-nuget-package-step.yml | 10 +-
.../libraries/ci-build-variables.yml | 10 +
eng/pipelines/libraries/common-variables.yml | 12 +-
src/Directory.Build.props | 1 -
.../Abstractions/README.md | 285 ++++++++++++++++++
.../Abstractions/doc/Sample.xml | 18 ++
.../Abstractions/src/Abstractions.csproj | 71 +++++
.../src/AbstractionsVersions.props | 70 +++++
.../Abstractions/src/Sample.cs | 20 ++
.../test/Abstractions.Test.csproj | 26 ++
.../Abstractions/test/SampleTest.cs | 19 ++
src/Microsoft.Data.SqlClient.sln | 45 ++-
.../add-ons/Directory.Build.props | 1 -
.../ref/Microsoft.Data.SqlClient.csproj | 16 +
.../src/Microsoft.Data.SqlClient.csproj | 16 +
.../netfx/ref/Microsoft.Data.SqlClient.csproj | 17 ++
.../netfx/src/Microsoft.Data.SqlClient.csproj | 17 ++
tools/props/Versions.props | 70 +++--
tools/specs/Microsoft.Data.SqlClient.nuspec | 4 +
...waysEncrypted.AzureKeyVaultProvider.nuspec | 34 ++-
tools/targets/GenerateMdsPackage.targets | 13 +
tools/targets/GenerateNugetPackage.targets | 31 --
.../targets/GenerateSqlServerPackage.targets | 13 +
.../GenerateAKVProviderNugetPackage.targets | 12 -
.../add-ons/GenerateAkvPackage.targets | 13 +
32 files changed, 941 insertions(+), 171 deletions(-)
create mode 100644 src/Microsoft.Data.SqlClient.Extensions/Abstractions/README.md
create mode 100644 src/Microsoft.Data.SqlClient.Extensions/Abstractions/doc/Sample.xml
create mode 100644 src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Abstractions.csproj
create mode 100644 src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/AbstractionsVersions.props
create mode 100644 src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Sample.cs
create mode 100644 src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/Abstractions.Test.csproj
create mode 100644 src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/SampleTest.cs
create mode 100644 tools/targets/GenerateMdsPackage.targets
delete mode 100644 tools/targets/GenerateNugetPackage.targets
create mode 100644 tools/targets/GenerateSqlServerPackage.targets
delete mode 100644 tools/targets/add-ons/GenerateAKVProviderNugetPackage.targets
create mode 100644 tools/targets/add-ons/GenerateAkvPackage.targets
diff --git a/.editorconfig b/.editorconfig
index cbc3fa7858..1b76fecaaa 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -162,7 +162,7 @@ dotnet_diagnostic.xUnit1031.severity=none
dotnet_diagnostic.xUnit1030.severity=none
# Xml files
-[*.{xml,csproj,stylecop,resx,ruleset,props,targets,config,nuspec}]
+[*.{xml,slnx,proj,csproj,stylecop,resx,ruleset,props,targets,config,nuspec}]
indent_size = 2
# Shell scripts
diff --git a/BUILDGUIDE.md b/BUILDGUIDE.md
index efeb747cad..7984813fb1 100644
--- a/BUILDGUIDE.md
+++ b/BUILDGUIDE.md
@@ -16,28 +16,33 @@ Once the environment is setup properly, execute the desired set of commands belo
### Targets
+The following build targets are defined in `build.proj`:
+
|Target|Description|
|-|-|
|`BuildAllConfigurations`|Default target. Builds the .NET Framework and .NET drivers for all target frameworks and operating systems.|
+|`BuildAbstractions`|Restore, build, and pack the Abstractions package, publishing the resulting NuGet into `packages/`.|
|`BuildNetCore`|Builds the .NET driver for all target frameworks.|
|`BuildNetCoreAllOS`|Builds the .NET driver for all target frameworks and operating systems.|
|`BuildNetFx`|Builds the .NET Framework driver for all target frameworks.|
|`BuildTestsNetCore`|Builds tests for the .NET driver.|
|`BuildTestsNetFx`|Builds tests for the .NET Framework driver.|
-|`Clean`|Cleans generated files.|
-|`Restore`|Restores Nuget packages.|
+|`Clean`|Cleans all generated files.|
+|`Restore`|Restores NuGet packages.|
|`RunTests`|Runs the unit, functional, and manual tests for the .NET Framework and .NET drivers|
|`RunUnitTests`|Runs just the unit tests for the .NET Framework and .NET drivers|
|`RunFunctionalTests`|Runs just the functional tests for the .NET Framework and .NET drivers|
|`RunManualTests`|Runs just the manual tests for the .NET Framework and .NET drivers|
|`BuildAkv`|Builds the Azure Key Vault Provider package for all supported platforms.|
-
### Parameters
+
+The following parameters may be defined as MSBuild properties to configure the
+build:
+
|Name|Supported Values|Default|Description|
|-|-|-|-|
|`Configuration`|`Debug`, `Release`|`Debug`|Sets the release configuration.|
-|`BuildNetFx`|`true`, `false`|`true` (Windows), `false` (other)|If false, skips building the .NET Framework driver on Windows.|
|`OSGroup`|`Unix`, `Windows_NT`, `AnyOS`|typically defaults to the client system's OS, unless using `BuildAllConfigurations` or an `AnyOS` specific target|The operating system to target.|
|`Platform`|`AnyCPU`, `x86`, `x64`, `ARM`, `ARM64`|`AnyCPU`|May only be set when using package reference type or running tests.|
|`TestSet`|`1`, `2`, `3`, `AE`|all|Build or run a subset of the manual tests. Omit (default) to target all tests.|
@@ -45,12 +50,11 @@ Once the environment is setup properly, execute the desired set of commands belo
|`TF`|`net8.0`, `net462`, `net47`, `net471`, `net472`, `net48`, `net481`|`net8.0` in netcore, `net462` in netfx|Sets the target framework when building or running tests. Not applicable when building the drivers.|
|`ResultsDirectory`|An absolute file path|./TestResults relative to current directory|Specifies where to write test results.|
-
## Example Workflows using MSBuild (Recommended)
+
Using the default configuration and running all tests:
```bash
-msbuild
msbuild -t:BuildTestsNetFx -p:TF=net462
msbuild -t:BuildTestsNetCore
msbuild -t:RunTests
@@ -59,28 +63,25 @@ msbuild -t:RunTests
Using the Release configuration:
```bash
-msbuild -p:configuration=Release
-msbuild -t:BuildTestsNetFx -p:TF=net462 -p:configuration=Release
-msbuild -t:BuildTestsNetCore -p:configuration=Release
-msbuild -t:RunTests -p:configuration=Release
+msbuild -t:BuildTestsNetFx -p:TF=net462 -p:Configuration=Release
+msbuild -t:BuildTestsNetCore -p:Configuration=Release
+msbuild -t:RunTests -p:Configuration=Release
```
Running only the unit tests:
```bash
-msbuild
msbuild -t:BuildTestsNetFx -p:TF=net462
msbuild -t:BuildTestsNetCore
msbuild -t:RunUnitTests
```
-Using a specific dotnet version/architecture:
+Using a specific .NET runtime to run tests:
```bash
-msbuild -p:configuration=Release
-msbuild -t:BuildTestsNetFx -p:TF=net462 -p:configuration=Release
-msbuild -t:BuildTestsNetCore -p:configuration=Release
-msbuild -t:RunTests -p:configuration=Release -p:DotnetPath=C:\net8-win-x86\
+msbuild -t:BuildTestsNetFx -p:TF=net462
+msbuild -t:BuildTestsNetCore
+msbuild -t:RunTests -p:DotnetPath=C:\net8-win-x86\
```
### Running Manual Tests
@@ -119,15 +120,13 @@ Manual Tests require the below setup to run:
|IsManagedInstance | (Optional) When set to `true` **TVP** related tests will use on non-Azure bs files to compare test results. this is needed when testing against Managed Instances or TVP Tests will fail on Test set 3. The default value is `false`. |
|PowerShellPath | The full path to PowerShell.exe. This is not required if the path is present in the PATH environment variable. | `D:\\escaped\\absolute\\path\\to\\PowerShell.exe` |
-
## Example workflows using the Dotnet SDK
-#### Run Functional Tests
+### Run Functional Tests
- Windows (`netfx x86`):
```bash
-msbuild
dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="x86" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests"
```
@@ -152,7 +151,8 @@ dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.S
```bash
dotnet test "src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="AnyCPU" -p:Configuration="Release" -p:TestTargetOS="Unixnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonlinuxtests&category!=nonuaptests"
```
-#### Run Manual Tests
+
+### Run Manual Tests
- Windows (`netfx x86`):
@@ -194,35 +194,39 @@ dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlCl
Tests can be built and run with custom "Reference Type" property that enables different styles of testing:
-- "Project" => Build and run tests with Microsoft.Data.SqlClient as Project Reference
-- "Package" => Build and run tests with Microsoft.Data.SqlClient as Package Reference with configured "TestMicrosoftDataSqlClientVersion" in "Versions.props" file.
+- "Project" => Build and run tests with Microsoft.Data.SqlClient as a Project Reference
+- "Package" => Build and run tests with Microsoft.Data.SqlClient as a Package Reference with configured "TestMicrosoftDataSqlClientVersion" in "Versions.props" file.
> ************** IMPORTANT NOTE BEFORE PROCEEDING WITH "PACKAGE" REFERENCE TYPE ***************
> CREATE A NUGET PACKAGE WITH BELOW COMMAND AND ADD TO LOCAL FOLDER + UPDATE NUGET CONFIG FILE TO READ FROM THAT LOCATION
>
> ```bash
-> msbuild -p:configuration=Release
+> msbuild -p:Configuration=Release
> ```
A non-AnyCPU platform reference can only be used with package reference type. Otherwise, the specified platform will be replaced with AnyCPU in the build process.
### Building Tests with Reference Type
-For .NET, all 4 reference types are supported:
+For .NET:
```bash
+# Project is the default reference type. The below commands are equivalent:
+msbuild -t:BuildTestsNetCore
msbuild -t:BuildTestsNetCore -p:ReferenceType=Project
-# Default setting uses Project Reference.
+# Package reference type:
msbuild -t:BuildTestsNetCore -p:ReferenceType=Package
```
-For .NET Framework, below reference types are supported:
+For .NET Framework:
```bash
+# Project is the default reference type. The below commands are equivalent:
+msbuild -t:BuildTestsNetFx -p:TF=net462
msbuild -t:BuildTestsNetFx -p:TF=net462 -p:ReferenceType=Project
-# Default setting uses Project Reference.
+# Package reference type:
msbuild -t:BuildTestsNetFx -p:TF=net462 -p:ReferenceType=Package
```
@@ -241,26 +245,25 @@ Tests can be built and run with custom Target Frameworks. See the below examples
### Building Tests with custom target framework
```bash
-msbuild -t:BuildTestsNetFx -p:TF=net462
# Build the tests for custom .NET Framework target
+msbuild -t:BuildTestsNetFx -p:TF=net462
```
```bash
-msbuild -t:BuildTestsNetCore -p:TF=net8.0
# Build the tests for custom .NET target
+msbuild -t:BuildTestsNetCore -p:TF=net8.0
```
### Running Tests with custom target framework (traditional)
```bash
+# Run tests with custom .NET Framework target
dotnet test -p:TargetNetFxVersion=net462 ...
-# Use above property to run Functional Tests with custom .NET Framework target
+# Run tests with custom .NET target
dotnet test -p:TargetNetCoreVersion=net8.0 ...
-# Use above property to run Functional Tests with custom .NET target
```
-
## Using Managed SNI on Windows
Managed SNI can be enabled on Windows by enabling the below AppContext switch:
@@ -285,20 +288,6 @@ When connecting to a server, if a protocol lower than TLS 1.2 is negotiated, a s
`Switch.Microsoft.Data.SqlClient.SuppressInsecureTLSWarning`
-### Troubleshooting Docker issues
-
-There may be times where connection cannot be made to SQL Server, we found below ideas helpful:
-
-- Clear Docker images to create clean image from time-to-time, and clear docker cache if needed by running `docker system prune` in Command Prompt.
-
-- If you face `Microsoft.Data.SqlClient.SNI.dll not found` errors when debugging, try updating the below properties in the netcore\Microsoft.Data.SqlClient.csproj file and try again:
-
- ```xml
- Unix
- false
- true
- ```
-
## Collecting Code Coverage
### Using VSTest
diff --git a/Directory.Packages.props b/Directory.Packages.props
index c339554838..28929a939a 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -5,6 +5,14 @@
+
+
+
+
+
+
diff --git a/NuGet.config b/NuGet.config
index 3233e60161..6c4f535818 100644
--- a/NuGet.config
+++ b/NuGet.config
@@ -1,7 +1,18 @@
+
+
-
+
+
+
diff --git a/build.proj b/build.proj
index 330a76be56..62154461a7 100644
--- a/build.proj
+++ b/build.proj
@@ -2,8 +2,9 @@
-
-
+
+
+
@@ -29,7 +30,7 @@
true
Configuration=$(Configuration);AssemblyVersion=$(SqlServerAssemblyVersion);AssemblyFileVersion=$(SqlServerAssemblyFileVersion);Version=$(SqlServerPackageVersion);
Configuration=$(Configuration);AssemblyFileVersion=$(AssemblyFileVersion);TargetsWindows=$(TargetsWindows);TargetsUnix=$(TargetsUnix);
- BuildProjectReferences=false;$(ProjectProperties);BuildForRelease=false;TargetNetCoreVersion=$(TargetNetCoreVersion);TargetNetFxVersion=$(TargetNetFxVersion)
+ $(ProjectProperties);BuildForRelease=false;TargetNetCoreVersion=$(TargetNetCoreVersion);TargetNetFxVersion=$(TargetNetFxVersion)
TestResults
+
+
@@ -89,32 +92,96 @@
-
-
-
+
+
+
-
-
+
+
+
+
+
+
+ AbstractionsPackageVersion=$(AbstractionsPackageVersion)
+
+
+
+
+ $(AbstractionsProperties);AbstractionsAssemblyFileVersion=$(AbstractionsAssemblyFileVersion)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
-
+
@@ -127,7 +194,10 @@
-
+
@@ -144,11 +214,15 @@
-
+
-
+
@@ -166,12 +240,17 @@
-
+
-
-
+
+
@@ -180,7 +259,9 @@
-
+
@@ -189,12 +270,18 @@
-
+
-
+
@@ -203,7 +290,10 @@
-
+
@@ -360,14 +450,14 @@
-
+
-
-
-
-
-
-
+
+
+
+
+
+
@@ -402,7 +492,10 @@
-
+
@@ -412,7 +505,9 @@
-
+
@@ -422,7 +517,9 @@
-
+
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 a482761610..486211a74f 100644
--- a/eng/pipelines/common/templates/jobs/build-signed-package-job.yml
+++ b/eng/pipelines/common/templates/jobs/build-signed-package-job.yml
@@ -55,6 +55,7 @@ jobs:
buildConfiguration: Release
OutputDirectory: $(artifactDirectory)
installNuget: false
+ properties: 'AbstractionsPackageVersion=$(abstractionsPackageVersion)'
- template: ../steps/esrp-code-signing-step.yml@self
parameters:
diff --git a/eng/pipelines/common/templates/jobs/ci-build-nugets-job.yml b/eng/pipelines/common/templates/jobs/ci-build-nugets-job.yml
index 7b72820534..0a0b62f7fd 100644
--- a/eng/pipelines/common/templates/jobs/ci-build-nugets-job.yml
+++ b/eng/pipelines/common/templates/jobs/ci-build-nugets-job.yml
@@ -60,6 +60,7 @@ jobs:
nuspecPath: 'tools/specs/Microsoft.Data.SqlClient.nuspec'
OutputDirectory: $(packagePath)
generateSymbolsPackage: false
+ properties: 'AbstractionsPackageVersion=$(abstractionsPackageVersion)'
displayName: 'Generate NuGet package M.D.SqlClient'
- template: ../steps/generate-nuget-package-step.yml@self
@@ -69,6 +70,7 @@ jobs:
nuspecPath: 'tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec'
OutputDirectory: $(packagePath)
generateSymbolsPackage: false
+ properties: 'MdsPackageVersion=$(mdsPackageVersion)'
installNuget: false
displayName: 'Generate NuGet package AKV Provider'
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 0754735e28..cc6518bb39 100644
--- a/eng/pipelines/common/templates/steps/generate-nuget-package-step.yml
+++ b/eng/pipelines/common/templates/steps/generate-nuget-package-step.yml
@@ -35,11 +35,11 @@ parameters:
type: boolean
default: true
- - name: referenceType
- default: project
- values:
- - project
- - package
+ # Semi-colon separated properties to pass to nuget via the -properties
+ # argument.
+ - name: properties
+ type: string
+ default: ''
steps:
- ${{ if parameters.installNuget }}:
diff --git a/eng/pipelines/libraries/ci-build-variables.yml b/eng/pipelines/libraries/ci-build-variables.yml
index 0d9154da16..dc5c1b8020 100644
--- a/eng/pipelines/libraries/ci-build-variables.yml
+++ b/eng/pipelines/libraries/ci-build-variables.yml
@@ -21,3 +21,13 @@ variables:
value: false
- name: packagePath
value: '$(Build.SourcesDirectory)/packages'
+
+ # TODO(ADO-38703): Remove these when the other pipeline changes arrive.
+ - name: baseBuildNumber
+ value: $[ split(variables['Build.BuildNumber'], '.')[0] ]
+ - name: abstractionsPackageVersion
+ value: 1.0.0.$(baseBuildNumber)
+ - name: mdsPackageVersion
+ value: $(NugetPackageVersion)
+ - name: akvPackageVersion
+ value: $(NugetPackageVersion)
diff --git a/eng/pipelines/libraries/common-variables.yml b/eng/pipelines/libraries/common-variables.yml
index 9dd1726c44..9eeedbff32 100644
--- a/eng/pipelines/libraries/common-variables.yml
+++ b/eng/pipelines/libraries/common-variables.yml
@@ -32,7 +32,7 @@ variables:
- name: Patch
value: '0'
- # Update this for preview releases.
+ # Update this for preview releases.
- name: Preview
value: '-preview'
- name: Revision
@@ -46,3 +46,13 @@ variables:
value: '$(Major).$(Minor)$(Patch).$(Build.BuildNumber)'
- name: nuspecPath
value: '$(REPOROOT)/tools/specs/Microsoft.Data.SqlClient.nuspec'
+
+ # TODO(ADO-38703): Remove these when the other pipeline changes arrive.
+ - name: baseBuildNumber
+ value: $[ split(variables['Build.BuildNumber'], '.')[0] ]
+ - name: abstractionsPackageVersion
+ value: 1.0.0.$(baseBuildNumber)
+ - name: mdsPackageVersion
+ value: $(NugetPackageVersion)
+ - name: akvPackageVersion
+ value: $(NugetPackageVersion)
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 16535cf9a7..1a41fd0a43 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -20,7 +20,6 @@
> msbuild -p:configuration=Release
-->
Project
- $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
$([System.IO.Path]::Combine('$(IntermediateOutputPath)','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)'))
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/README.md b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/README.md
new file mode 100644
index 0000000000..fbb8bfb738
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/README.md
@@ -0,0 +1,285 @@
+# MDS Azure Extension Design
+
+## Overview
+
+For the MDS 7.0.0 release, we are proposing the following package architecture
+changes that will decouple several large dependencies from MDS and move them
+into a new `Azure` extension package:
+
+- Create a new `Abstractions` package that all other MDS packages depend on.
+ - This will contain types and definitions common to the other MDS packages,
+ such as base classes, enums, delegates, etc.
+- Create a new `Azure` package that will own the following implementations:
+ - Azure Authentication
+ - Azure Attestation
+ - Azure Key Vault interactions
+- Move the above implementations out of MDS and into the new `Azure` package.
+- Move the existing `AzureKeyVaultProvider` (AKV) implementation into the new
+ `Azure` extension package.
+
+This will reduce the main MDS package dependency tree along with a moderate
+package size reduction.
+
+## Motivation
+
+Issue: [#1108](https://github.com/dotnet/SqlClient/issues/1108)
+
+Customers and the developer community have voiced concerns with MDS being
+tightly coupled to Azure dependencies. Many customers do not use Azure and do
+not want to deploy unnecessary DLLs with their applications.
+
+Moving the Azure dependent implementations into a separate `Azure` extension
+package achieves two goals:
+
+- Remove Azure packages as direct dependencies of MDS and reduce the MDS
+ dependency tree.
+- Clearly expose existing MDS extension points, prove their functionality, and
+ demonstrate how to use them.
+
+The following dependencies will be removed from the main MDS package:
+
+- `Azure.Identity`
+ - `Azure.Core` (transitive)
+ - `Microsoft.Identity.Client` (transitive)
+- `Microsoft.IdentityModel.JsonWebTokens`
+ - `Microsoft.IdentityModel.Tokens` (transitive)
+ - `Microsoft.IdentityModel.Logging` (transitive)
+- `Microsoft.IdentityModel.Protocols.OpenIdConnect`
+ - `Microsoft.IdentityModel.Protocols` (transitive)
+
+The following dependencies will be removed from the AKV Provider package:
+
+- `Azure.Core`
+- `Azure.Security.KeyVault.Keys`
+
+## Package Architecture
+
+```mermaid
+classDiagram
+class MDS
+class MDS.Extensions.Abstractions
+class MDS.Extensions.Azure
+class AKV Provider
+
+MDS --> MDS.Extensions.Abstractions
+MDS ..> MDS.Extensions.Azure
+MDS ..> AKV Provider
+MDS.Extensions.Azure --> MDS.Extensions.Abstractions
+AKV Provider --> MDS.Extensions.Azure
+
+MDS: Depend on MDS.Extensions.Abstractions
+MDS: Load Azure or AKV assembly
+MDS.Extensions.Abstractions: Azure Authentication Types
+MDS.Extensions.Abstractions: Azure Attestation Types
+MDS.Extensions.Abstractions: Azure Key Vault Types
+MDS.Extensions.Azure: Depend on MDS.Extensions.Abstractions
+MDS.Extensions.Azure: Authentication Implementation
+MDS.Extensions.Azure: Attestation Implementation
+MDS.Extensions.Azure: Key Vault Implementation
+AKV Provider: Depend on MDS.Extensions.Azure
+```
+
+In previous MDS versions, the AKV package depended directly on the main MDS
+package through a ranged version (for example [6.0.0, 7.0.0) - all 6.x
+versions). With the new package architecture this is no longer the case.
+Extension packages will not depend on the main MDS package, nor will the main
+MDS package depend on any extension packages. All dependencies between MDS and
+its extensions will occur through the `Abstractions` package.
+
+This new looser coupling gives applications the flexibility to depend on only
+the main MDS package, or on MDS and a subset of it extension packages if
+desired.
+
+## Consuming
+
+There are several ways that applications may consume MDS and its extensions:
+
+- MDS without Azure features
+- MDS with MDS-supplied Azure features
+- MDS with externally supplied Azure features
+
+Applications never need to directly depend on the `Abstractions` base package.
+This will be transitively depended on by other MDS packages.
+
+### Without Azure Features
+
+Applications that do not use any Azure features will no longer bring in those
+unwanted dependencies transitively. Simply include the main MDS package by
+itself:
+
+```xml
+
+
+
+```
+
+Calls to MDS APIs that require Azure features will throw an exception, since
+no Azure feature implementation is present.
+
+### With MDS Azure Features
+
+Applications that wish to use MDS-supplied Azure features will need to include
+the new `Azure` extension package as a direct dependency alongside the main MDS
+package:
+
+```xml
+
+
+
+
+```
+
+MDS will automatically detect the `Azure` extension assemblies and load them.
+
+### With External Azure Features
+
+Applications that wish to use Azure features supplied by another (non-MDS)
+package will need to include that package as a direct dependency alongside the
+main MDS package:
+
+```xml
+
+
+
+
+```
+
+Additionally, applications will need to instruct MDS to use the external Azure
+feature implementations via the appropriate APIs at runtime:
+
+- Authentication: [SqlAuthenticationProvider](https://learn.microsoft.com/en-us/dotnet/api/microsoft.data.sqlclient.sqlauthenticationprovider?view=sqlclient-dotnet-core-6.0)
+- Attestation: _**New API will be exposed.**_
+- Key Valut: [SqlColumnEncryptionKeyStoreProvider](https://learn.microsoft.com/en-us/dotnet/api/microsoft.data.sqlclient.sqlcolumnencryptionkeystoreprovider?view=sqlclient-dotnet-core-6.0)
+
+## Versioning Strategy
+
+The MDS suite of packages will be versioned independently. This provides
+flexibility to update APIs and implementations for packages as needed, avoiding
+unnecessary version bumps and releases. The initial release of these packages
+will have the following versions:
+
+|Package|Version|Comment|
+|-|-|-|
+|`Microsoft.Data.SqlClient.Extensions.Abstractions`|1.0.0|First version of this package.|
+|`Microsoft.Data.SqlClient`|7.0.0|Major version bump due to breaking changes described in this document.|
+|`Microsoft.Data.SqlClient.Extensions.Azure`|1.0.0|First version of this package.|
+|`Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider`|7.0.0|_**Deprecated.**_|
+
+Going forward, each package will be versioned appropriately based on the nature
+of the changes included with subsequent releases.
+
+**Note**: The `AzureKeyVaultProvider` package will remain at 7.0.0. It will be
+deprecated and eventually removed, as it has been replaced by the `Azure`
+extension package.
+
+## Intradependence
+
+The main MDS package and the new `Azure` package will depend on the
+`Abstractions` package. When APIs are added, modified, or removed from the
+`Abstractions` package, corresponding changes will be made to the dependent
+packages as well. Those dependent packages will then take a strict dependency
+on the appropriate `Abstractions` package version. This ensures that only
+compatible extensions package versions can co-exist with the main MDS package.
+
+For example, imagine that a new extensible conenction pooling feature is added
+to MDS. The `Abstractions` package would be updated to include any new pooling
+APIs, the main MDS package would be updated to accept extensible pooling, and
+the new pooling implementation would be included in a new `ConnectionPooling`
+extension package. The versions of these packages would look something like
+this:
+
+|Package|Version|
+|-|-|
+|`Microsoft.Data.SqlClient.Extensions.Abstractions`|1.1.0|
+|`Microsoft.Data.SqlClient`|7.1.0|
+|`Microsoft.Data.SqlClient.Extensions.ConnectionPooling`|1.0.0|
+
+Both the main MDS package and the new `ConnectionPooling` package would depend
+on `Abstractions` v1.1.0.
+
+An application wishing to use the new `ConnectionPooling` v1.0.0 package must
+also update the main MDS package to v7.1.0. The application would not be able
+to use `ConnectionPooling` v1.0.0 and MDS v7.0.0.
+
+## Backwards Compatibility
+
+There are several backwards compatibility scenarios to consider for applications
+that rely on MDS Azure features currently implemented in the main MDS package
+and the AKV package. The new extensions package architecture aims to reduce the
+friction for these apps, but not all scenarios will be seamless.
+
+All of the scenarios below assume that the application is upgrading to the
+latest versions of MDS packages.
+
+### Apps using MDS Azure Authentication
+
+Applications currently using the MDS-supplied Azure Authentication features will
+need to add a dependency on the `Azure` extension package to their project
+alongside the main MDS package:
+
+```xml
+
+
+
+
+```
+
+All Azure Authentication namespaces and types will remain the same, so this
+should be the only change necessary for applications.
+
+### Apps using MDS Azure Attestation
+
+Applications currently using the MDS-supplied Azure Attestation features will
+need to add a dependency on the `Azure` extension package to their project
+alongside the main MDS package:
+
+```xml
+
+
+
+
+```
+
+All Azure Attestation namespaces and types will remain the same, so this should
+be the only change necessary for applications.
+
+### Apps using AKV Provider
+
+Applications currently using the MDS-supplied AKV provider will have two options
+when upgrading to MDS v7.0.0. Both options rely on the main MDS package finding
+and loading an appropriate DLL (assembly) at runtime. The absence of an
+appropriate DLL will cause Azure Key Vault operations to throw an exception.
+
+#### Use Azure Extension
+
+This is the preferred approach. The application would be updated to depend
+on the main MDS package and the `Azure` extension package:
+
+```xml
+
+
+
+
+```
+
+The `Azure` extension package will contain the same namespaces and types as the
+current AKV provider and will be a drop-in replacement. The main MDS v7.0.0
+package will look for the `Azure` extension assembly and automatically load it.
+
+#### Use AKV Provider v7.0.0
+
+This is a temporary solution. The AKV provider v7.0.0 will be marked as
+deprecated and removed entirely at some point in the future. The applictaion
+would remain dependent on the AKV provider, but must update to the v7.0.0
+package. Previous AKV package versions do not support main MDS package versions
+beyond the v6.x range.
+
+```xml
+
+
+
+
+```
+
+This AKV Provider v7.0.0 package will be empty and simply depend on the `Azure`
+extension package to transitively provide the Azure Key Vault features.
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/doc/Sample.xml b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/doc/Sample.xml
new file mode 100644
index 0000000000..8d5f5c44d5
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/doc/Sample.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+ Sample class to demonstrate packaging and pipelines.
+
+
+
+ Construct with a name.
+ The name.
+
+
+ Gets the name.
+ The name.
+
+
+
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Abstractions.csproj b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Abstractions.csproj
new file mode 100644
index 0000000000..485a3de83a
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Abstractions.csproj
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+ netstandard2.0
+
+
+
+
+ enable
+ enable
+
+
+
+
+ Microsoft.Data.SqlClient.Extensions.Abstractions
+ Microsoft.Data.SqlClient.Extensions.Abstractions
+
+
+ $(_DefaultMajorVersion).0.0.0
+
+ $(AbstractionsAssemblyFileVersion)
+ $(AbstractionsAssemblyFileVersion)
+ $(AbstractionsPackageVersion)
+
+
+
+
+ <_Parameter1>true
+
+
+
+
+
+
+
+
+ $(AssemblyName)
+ $(AbstractionsPackageVersion)
+ $(PackagesDir)
+ true
+ snupkg
+
+ Microsoft Corporation
+ Microsoft Corporation
+ Microsoft.Data.SqlClient Extensions Abstractions
+ https://github.com/dotnet/SqlClient
+ MIT
+ dotnet.png
+
+
+
+
+
+
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/AbstractionsVersions.props b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/AbstractionsVersions.props
new file mode 100644
index 0000000000..29ff5899af
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/AbstractionsVersions.props
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ <_DefaultMajorVersion>1
+
+
+ <_OurPackageVersion Condition="'$(AbstractionsPackageVersion)' != ''">$(AbstractionsPackageVersion)
+ <_OurPackageVersion Condition="'$(AbstractionsPackageVersion)' == ''">$(_DefaultMajorVersion).0.0.$(BuildNumber)-dev
+
+
+
+ <_OurAssemblyFileVersion Condition="'$(AbstractionsAssemblyFileVersion)' != ''">$(AbstractionsAssemblyFileVersion)
+
+ <_OurAssemblyFileVersion Condition="'$(AbstractionsAssemblyFileVersion)' == '' and '$(AbstractionsPackageVersion)' != ''">$(AbstractionsPackageVersion.Split('-')[0])
+
+ <_OurAssemblyFileVersion Condition="'$(AbstractionsAssemblyFileVersion)' == '' and '$(AbstractionsPackageVersion)' == ''">$(_DefaultMajorVersion).0.0.$(BuildNumber)
+
+
+ $(_OurPackageVersion)
+ $(_OurAssemblyFileVersion)
+
+
+
+
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Sample.cs b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Sample.cs
new file mode 100644
index 0000000000..bf22119436
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Sample.cs
@@ -0,0 +1,20 @@
+namespace Microsoft.Data.SqlClient.Extensions.Abstractions;
+
+///
+public class Sample
+{
+ ///
+ public Sample(string name)
+ {
+ Name = name;
+ }
+
+ ///
+ public string Name { get; private set; }
+
+ // Update the name.
+ internal void SetName(string name)
+ {
+ Name = name;
+ }
+}
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/Abstractions.Test.csproj b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/Abstractions.Test.csproj
new file mode 100644
index 0000000000..118b215737
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/Abstractions.Test.csproj
@@ -0,0 +1,26 @@
+
+
+
+ net462;net47;net471;net472;net48;net481;net8.0;net9.0
+ enable
+ enable
+ false
+ true
+ Microsoft.Data.SqlClient.Extensions.Abstractions.Test
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/SampleTest.cs b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/SampleTest.cs
new file mode 100644
index 0000000000..ab8e9da052
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/SampleTest.cs
@@ -0,0 +1,19 @@
+namespace Microsoft.Data.SqlClient.Extensions.Abstractions.Test;
+
+public class SampleTest
+{
+ [Fact]
+ public void Construction()
+ {
+ Assert.Equal("test", new Sample("test").Name);
+ }
+
+ [Fact]
+ public void SetName()
+ {
+ var sample = new Sample("test");
+ Assert.Equal("test", sample.Name);
+ sample.SetName("new name");
+ Assert.Equal("new name", sample.Name);
+ }
+}
diff --git a/src/Microsoft.Data.SqlClient.sln b/src/Microsoft.Data.SqlClient.sln
index 8b4f420f19..9de2a7feaa 100644
--- a/src/Microsoft.Data.SqlClient.sln
+++ b/src/Microsoft.Data.SqlClient.sln
@@ -1,6 +1,7 @@
-Microsoft Visual Studio Solution File, Format Version 12.00
+
+Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
-VisualStudioVersion = 17.0.31912.275
+VisualStudioVersion = 17.14.36203.30
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.SqlClient", "Microsoft.Data.SqlClient\netfx\src\Microsoft.Data.SqlClient.csproj", "{407890AC-9876-4FEF-A6F1-F36A876BAADE}"
EndProject
@@ -314,6 +315,17 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SqlClient.Stress.Runner", "Microsoft.Data.SqlClient\tests\StressTests\SqlClient.Stress.Runner\SqlClient.Stress.Runner.csproj", "{4A9C11F4-9577-ABEC-C070-83A194746D9B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SqlClient.Stress.Tests", "Microsoft.Data.SqlClient\tests\StressTests\SqlClient.Stress.Tests\SqlClient.Stress.Tests.csproj", "{FAA1E517-581A-D3DC-BAC9-FAD1D5A5142C}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.SqlClient.Extensions", "Microsoft.Data.SqlClient.Extensions", "{19F1F1E5-3013-7660-661A-2A15F7D606C1}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Abstractions", "Abstractions", "{556B486E-F9B0-7EA9-6A25-DA560C312761}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{210228A5-979A-DE06-EE1F-B35C65E1583C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Abstractions", "Microsoft.Data.SqlClient.Extensions\Abstractions\src\Abstractions.csproj", "{B21E7C94-D805-427E-928A-8DE8EA2F08CC}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{59667E4C-0BD2-9F48-FB50-9E55DD8B1011}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Abstractions.Test", "Microsoft.Data.SqlClient.Extensions\Abstractions\test\Abstractions.Test.csproj", "{04ACBF75-CFF2-41AB-B652-776BC0533490}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Data.SqlClient.Samples", "..\doc\Samples\Microsoft.Data.SqlClient.Samples.csproj", "{C09B9D2F-E463-BEBD-34E4-E8F2C201A277}"
EndProject
@@ -657,6 +669,30 @@ Global
{C09B9D2F-E463-BEBD-34E4-E8F2C201A277}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C09B9D2F-E463-BEBD-34E4-E8F2C201A277}.Release|x64.ActiveCfg = Release|x64
{C09B9D2F-E463-BEBD-34E4-E8F2C201A277}.Release|x86.ActiveCfg = Release|x86
+ {B21E7C94-D805-427E-928A-8DE8EA2F08CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B21E7C94-D805-427E-928A-8DE8EA2F08CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B21E7C94-D805-427E-928A-8DE8EA2F08CC}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {B21E7C94-D805-427E-928A-8DE8EA2F08CC}.Debug|x64.Build.0 = Debug|Any CPU
+ {B21E7C94-D805-427E-928A-8DE8EA2F08CC}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {B21E7C94-D805-427E-928A-8DE8EA2F08CC}.Debug|x86.Build.0 = Debug|Any CPU
+ {B21E7C94-D805-427E-928A-8DE8EA2F08CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B21E7C94-D805-427E-928A-8DE8EA2F08CC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B21E7C94-D805-427E-928A-8DE8EA2F08CC}.Release|x64.ActiveCfg = Release|Any CPU
+ {B21E7C94-D805-427E-928A-8DE8EA2F08CC}.Release|x64.Build.0 = Release|Any CPU
+ {B21E7C94-D805-427E-928A-8DE8EA2F08CC}.Release|x86.ActiveCfg = Release|Any CPU
+ {B21E7C94-D805-427E-928A-8DE8EA2F08CC}.Release|x86.Build.0 = Release|Any CPU
+ {04ACBF75-CFF2-41AB-B652-776BC0533490}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {04ACBF75-CFF2-41AB-B652-776BC0533490}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {04ACBF75-CFF2-41AB-B652-776BC0533490}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {04ACBF75-CFF2-41AB-B652-776BC0533490}.Debug|x64.Build.0 = Debug|Any CPU
+ {04ACBF75-CFF2-41AB-B652-776BC0533490}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {04ACBF75-CFF2-41AB-B652-776BC0533490}.Debug|x86.Build.0 = Debug|Any CPU
+ {04ACBF75-CFF2-41AB-B652-776BC0533490}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {04ACBF75-CFF2-41AB-B652-776BC0533490}.Release|Any CPU.Build.0 = Release|Any CPU
+ {04ACBF75-CFF2-41AB-B652-776BC0533490}.Release|x64.ActiveCfg = Release|Any CPU
+ {04ACBF75-CFF2-41AB-B652-776BC0533490}.Release|x64.Build.0 = Release|Any CPU
+ {04ACBF75-CFF2-41AB-B652-776BC0533490}.Release|x86.ActiveCfg = Release|Any CPU
+ {04ACBF75-CFF2-41AB-B652-776BC0533490}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -714,6 +750,11 @@ Global
{4A9C11F4-9577-ABEC-C070-83A194746D9B} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{FAA1E517-581A-D3DC-BAC9-FAD1D5A5142C} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{C09B9D2F-E463-BEBD-34E4-E8F2C201A277} = {ED952CF7-84DF-437A-B066-F516E9BE1C2C}
+ {556B486E-F9B0-7EA9-6A25-DA560C312761} = {19F1F1E5-3013-7660-661A-2A15F7D606C1}
+ {210228A5-979A-DE06-EE1F-B35C65E1583C} = {556B486E-F9B0-7EA9-6A25-DA560C312761}
+ {B21E7C94-D805-427E-928A-8DE8EA2F08CC} = {210228A5-979A-DE06-EE1F-B35C65E1583C}
+ {59667E4C-0BD2-9F48-FB50-9E55DD8B1011} = {556B486E-F9B0-7EA9-6A25-DA560C312761}
+ {04ACBF75-CFF2-41AB-B652-776BC0533490} = {59667E4C-0BD2-9F48-FB50-9E55DD8B1011}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {01D48116-37A2-4D33-B9EC-94793C702431}
diff --git a/src/Microsoft.Data.SqlClient/add-ons/Directory.Build.props b/src/Microsoft.Data.SqlClient/add-ons/Directory.Build.props
index 7776439adc..dfeb60b38c 100644
--- a/src/Microsoft.Data.SqlClient/add-ons/Directory.Build.props
+++ b/src/Microsoft.Data.SqlClient/add-ons/Directory.Build.props
@@ -9,7 +9,6 @@
true
true
Project
- $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
true
$([System.IO.Path]::Combine('$(IntermediateOutputPath)','$(TargetFramework)','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)'))
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 e6ffc2526b..011fdafffb 100644
--- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj
@@ -50,6 +50,22 @@
+
+
+
+
+
+
+
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 c7983fcc15..cddab6fd9f 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
@@ -1088,6 +1088,22 @@
+
+
+
+
+
+
+
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 1d9c1985bd..19be9bb567 100644
--- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj
@@ -49,5 +49,22 @@
+
+
+
+
+
+
+
+
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 ea6b7de6ce..ce36beb46e 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
@@ -1079,8 +1079,25 @@
+
+
+
+
+
+
+
+
diff --git a/tools/props/Versions.props b/tools/props/Versions.props
index ad42c4964a..7a3ba3cc9a 100644
--- a/tools/props/Versions.props
+++ b/tools/props/Versions.props
@@ -1,29 +1,12 @@
-
+
+
+
- 7.0.0
0
-
-
- $(MdsVersionDefault).$(BuildNumber)
-
- 7.0.0.0
-
- $(AssemblyFileVersion)
- $(MdsVersionDefault)-dev
- $(NugetPackageVersion)
+
+
@@ -33,8 +16,47 @@
1.0.0-dev
$(SqlServerPackageVersion)
+
+
+
+
+
+
+ 7.0.0
+
+ $(MdsVersionDefault).$(BuildNumber)-dev
+
+
+
+
+ $(MdsVersionDefault).$(BuildNumber)
+
+
+ 7.0.0.0
+
+ $(AssemblyFileVersion)
+
+
+ $(MdsPackageVersion)
+
+
+
- $(NugetPackageVersion)
+ 7.0.0
+ $(AkvVersionDefault).$(BuildNumber)-dev
- $(NugetPackageVersion)
+ $(MdsPackageVersion)
diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec
index 9cb42a9ed6..18921fc2be 100644
--- a/tools/specs/Microsoft.Data.SqlClient.nuspec
+++ b/tools/specs/Microsoft.Data.SqlClient.nuspec
@@ -32,6 +32,7 @@
+
@@ -49,6 +50,7 @@
+
@@ -61,6 +63,7 @@
+
@@ -73,6 +76,7 @@
+
diff --git a/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec b/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec
index 0816fd3086..a313d0af12 100644
--- a/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec
+++ b/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec
@@ -25,17 +25,23 @@ Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyStoreProvider.SqlColumnEncrypti
sqlclient microsoft.data.sqlclient azurekeyvaultprovider akvprovider alwaysencrypted
-
+
-
+
+
+
+
+
+
+
@@ -46,22 +52,22 @@ Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyStoreProvider.SqlColumnEncrypti
-
-
-
-
-
+
+
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
+
+
+
+
+
+
+
+
+
diff --git a/tools/targets/GenerateNugetPackage.targets b/tools/targets/GenerateNugetPackage.targets
deleted file mode 100644
index 4c8cea4159..0000000000
--- a/tools/targets/GenerateNugetPackage.targets
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
- $(NugetPackageVersion)-debug
-
-
-
-
-
-
-
-
-
-
-
-
- $(SqlServerPackageVersion)-debug
-
-
-
-
-
-
-
-
-
-
-
diff --git a/tools/targets/GenerateSqlServerPackage.targets b/tools/targets/GenerateSqlServerPackage.targets
new file mode 100644
index 0000000000..ea6655dcee
--- /dev/null
+++ b/tools/targets/GenerateSqlServerPackage.targets
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/targets/add-ons/GenerateAKVProviderNugetPackage.targets b/tools/targets/add-ons/GenerateAKVProviderNugetPackage.targets
deleted file mode 100644
index 78da74bf32..0000000000
--- a/tools/targets/add-ons/GenerateAKVProviderNugetPackage.targets
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
- $(NugetPackageVersion)-debug
-
-
-
-
-
-
diff --git a/tools/targets/add-ons/GenerateAkvPackage.targets b/tools/targets/add-ons/GenerateAkvPackage.targets
new file mode 100644
index 0000000000..fd822442a2
--- /dev/null
+++ b/tools/targets/add-ons/GenerateAkvPackage.targets
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
From 57312df06f7161d8d7a1fe48831cf1d35faf23e7 Mon Sep 17 00:00:00 2001
From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com>
Date: Thu, 16 Oct 2025 13:23:37 -0300
Subject: [PATCH 02/13] Abstractions Package - Pipeline Changes (#3628)
---
build.proj | 1 -
eng/pipelines/akv-official-pipeline.yml | 9 +-
.../jobs/build-signed-package-job.yml | 54 ++++-
.../templates/jobs/ci-build-nugets-job.yml | 87 ++++++--
.../templates/jobs/ci-run-tests-job.yml | 110 ++++++----
.../jobs/run-tests-package-reference-job.yml | 8 +-
.../jobs/validate-signed-package-job.yml | 84 +++-----
.../templates/stages/ci-run-tests-stage.yml | 62 ++++--
...ld-all-configurations-signed-dlls-step.yml | 34 ++--
.../templates/steps/build-all-tests-step.yml | 48 ++---
.../build-and-run-tests-netcore-step.yml | 12 +-
.../steps/build-and-run-tests-netfx-step.yml | 12 +-
.../templates/steps/ci-prebuild-step.yml | 30 +--
.../templates/steps/ci-project-build-step.yml | 39 ++--
.../templates/steps/code-analyze-step.yml | 50 ++---
.../steps/copy-dlls-for-test-step.yml | 4 +-
.../steps/generate-nuget-package-step.yml | 17 +-
.../templates/steps/publish-symbols-step.yml | 2 +-
.../templates/steps/run-all-tests-step.yml | 22 +-
.../update-nuget-config-local-feed-step.yml | 80 +++-----
eng/pipelines/dotnet-sqlclient-ci-core.yml | 190 +++++++++++-------
...qlclient-ci-package-reference-pipeline.yml | 2 +-
...qlclient-ci-project-reference-pipeline.yml | 2 +-
.../dotnet-sqlclient-signing-pipeline.yml | 15 +-
eng/pipelines/jobs/build-akv-official-job.yml | 37 ++--
.../jobs/pack-abstractions-package-ci-job.yml | 148 ++++++++++++++
eng/pipelines/jobs/stress-tests-ci-job.yml | 2 +-
.../jobs/test-abstractions-package-ci-job.yml | 174 ++++++++++++++++
.../libraries/ci-build-variables.yml | 19 +-
eng/pipelines/libraries/common-variables.yml | 51 +++--
.../libraries/mds-validation-variables.yml | 2 +-
.../build-abstractions-package-ci-stage.yml | 119 +++++++++++
.../steps/compound-build-akv-step.yml | 18 +-
.../steps/compound-nuget-pack-step.yml | 14 +-
.../steps/roslyn-analyzers-akv-step.yml | 15 +-
.../variables/akv-official-variables.yml | 6 +-
.../ref/Microsoft.Data.SqlClient.csproj | 3 -
.../src/Microsoft.Data.SqlClient.csproj | 3 -
.../netfx/ref/Microsoft.Data.SqlClient.csproj | 3 -
.../netfx/src/Microsoft.Data.SqlClient.csproj | 3 -
tools/props/Versions.props | 6 +-
tools/specs/Microsoft.Data.SqlClient.nuspec | 143 ++++++-------
...waysEncrypted.AzureKeyVaultProvider.nuspec | 35 ++--
tools/targets/GenerateMdsPackage.targets | 2 +-
.../add-ons/GenerateAkvPackage.targets | 2 +-
45 files changed, 1187 insertions(+), 592 deletions(-)
create mode 100644 eng/pipelines/jobs/pack-abstractions-package-ci-job.yml
create mode 100644 eng/pipelines/jobs/test-abstractions-package-ci-job.yml
create mode 100644 eng/pipelines/stages/build-abstractions-package-ci-stage.yml
diff --git a/build.proj b/build.proj
index 62154461a7..7fe296591a 100644
--- a/build.proj
+++ b/build.proj
@@ -456,7 +456,6 @@
-
diff --git a/eng/pipelines/akv-official-pipeline.yml b/eng/pipelines/akv-official-pipeline.yml
index d7bc900bb8..316ca9e6ea 100644
--- a/eng/pipelines/akv-official-pipeline.yml
+++ b/eng/pipelines/akv-official-pipeline.yml
@@ -105,7 +105,8 @@ extends:
roslyn:
enabled: ${{ parameters.runSdlTasks }}
break: true
- # Requires RoslynAnalyzers task to be added after build task
+ # Requires RoslynAnalyzers task to be added somewhere in
+ # the build stage.
publishLogs:
enabled: ${{ parameters.runSdlTasks }}
@@ -113,7 +114,7 @@ extends:
sbom:
enabled: ${{ parameters.runSdlTasks }}
packageName: 'Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider'
- packageVersion: ${{ variables.nugetPackageVersion }}
+ packageVersion: ${{ variables.akvPackageVersion }}
tsa:
# OneBranch publishes all sdl results to TSA. If TSA is disabled all SDL tools will
@@ -127,11 +128,11 @@ extends:
jobs:
- template: /eng/pipelines/jobs/build-akv-official-job.yml@self
parameters:
+ akvAssemblyFileVersion: '${{ variables.assemblyFileVersion }}'
+ akvPackageVersion: '${{ variables.akvPackageVersion }}'
apiScanDllPath: '${{ variables.apiScanDllPath }}'
apiScanPdbPath: '${{ variables.apiScanPdbPath }}'
- assemblyFileVersion: '${{ variables.assemblyFileVersion }}'
buildConfiguration: '${{ parameters.buildConfiguration }}'
- nugetPackageVersion: '${{ variables.nugetPackageVersion }}'
mdsPackageVersion: '${{ variables.mdsPackageVersion }}'
publishSymbols: '${{ parameters.publishSymbols }}'
signingAppRegistrationClientId: '$(SigningAppRegistrationClientId)'
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 486211a74f..276893cc4d 100644
--- a/eng/pipelines/common/templates/jobs/build-signed-package-job.yml
+++ b/eng/pipelines/common/templates/jobs/build-signed-package-job.yml
@@ -27,8 +27,10 @@ jobs:
variables:
- template: ../../../libraries/variables.yml@self
- ${{ if parameters.isPreview }}:
- - name: NugetPackageVersion
- value: $(PreviewNugetPackageVersion)
+ - name: abstractionsPackageVersion
+ value: $(abstractionsPackagePreviewVersion)
+ - name: mdsPackageVersion
+ value: $(previewMdsPackageVersion)
steps:
- script: SET
@@ -37,15 +39,45 @@ jobs:
- powershell: |
Write-Host "##vso[task.setvariable variable=CDP_BUILD_TYPE_COPY;isOutput=true]$($env:CDP_BUILD_TYPE)"
name: GetBuildType
+
+ # Build our tooling, which is required by the analysis step below, but
+ # shouldn't be analyzed itself.
+ - task: MSBuild@1
+ displayName: 'Build Tooling'
+ inputs:
+ solution: '**/build.proj'
+ configuration: $(Configuration)
+ msbuildArguments: -t:BuildTools
+
+ # Perform analysis before building, since this step will clobber build output
+ - template: ../steps/code-analyze-step.yml@self
- - template: ../steps/build-all-configurations-signed-dlls-step.yml@self
+ # Update the root NuGet.config to use the packages/ directory as a source.
+ # When we build MDS in Package mode, it depends on the Abstractions package,
+ # which we will build and package into packages/.
+ - template: ../steps/update-nuget-config-local-feed-step.yml
parameters:
- buildConfiguration: Release
+ packagePath: $(REPOROOT)/packages
- - template: ../steps/code-analyze-step.yml@self
+ # Build the Abstractions package.
+ - task: MSBuild@1
+ displayName: Build Abstractions
+ inputs:
+ solution: '**/build.proj'
+ configuration: $(Configuration)
+ msbuildArguments: -t:BuildAbstractions
+
+ # Build MDS in Package mode, producing signed DLLs.
+ - template: ../steps/build-all-configurations-signed-dlls-step.yml@self
parameters:
- analyzeType: all
-
+ # These variables are sourced from common-variables.yml.
+ abstractionsAssemblyFileVersion: $(abstractionsAssemblyFileVersion)
+ abstractionsPackageVersion: $(abstractionsPackageVersion)
+ configuration: $(Configuration)
+ mdsAssemblyFileVersion: $(mdsAssemblyFileVersion)
+ mdsPackageVersion: $(mdsPackageVersion)
+ referenceType: Package
+
- template: ../steps/esrp-code-signing-step.yml@self
parameters:
artifactType: dll
@@ -53,9 +85,13 @@ jobs:
- template: ../steps/generate-nuget-package-step.yml@self
parameters:
buildConfiguration: Release
- OutputDirectory: $(artifactDirectory)
+ displayName: 'Create MDS NuGet Package'
installNuget: false
+ nuspecPath: $(nuspecPath)
+ outputDirectory: $(artifactDirectory)
+ packageVersion: $(mdsPackageVersion)
properties: 'AbstractionsPackageVersion=$(abstractionsPackageVersion)'
+ referenceType: Package
- template: ../steps/esrp-code-signing-step.yml@self
parameters:
@@ -71,4 +107,4 @@ jobs:
parameters:
buildConfiguration: Release
publishSymbols: ${{ parameters['PublishSymbols'] }}
- symbolsArtifactName: mds_symbols_$(System.TeamProject)_$(Build.Repository.Name)_$(Build.SourceBranchName)_$(NuGetPackageVersion)_$(System.TimelineId)
+ symbolsArtifactName: mds_symbols_$(System.TeamProject)_$(Build.Repository.Name)_$(Build.SourceBranchName)_$(mdsPackageVersion)_$(System.TimelineId)
diff --git a/eng/pipelines/common/templates/jobs/ci-build-nugets-job.yml b/eng/pipelines/common/templates/jobs/ci-build-nugets-job.yml
index 0a0b62f7fd..da83c2077c 100644
--- a/eng/pipelines/common/templates/jobs/ci-build-nugets-job.yml
+++ b/eng/pipelines/common/templates/jobs/ci-build-nugets-job.yml
@@ -4,6 +4,17 @@
# See the LICENSE file in the project root for more information. #
#################################################################################
parameters:
+
+ - name: 'debug'
+ type: boolean
+ default: false
+
+ - name: referenceType
+ type: string
+ values:
+ - Package
+ - Project
+
- name: poolName
type: string
default: $(ci_var_defaultPoolName)
@@ -12,9 +23,13 @@ parameters:
type: string
default: ADO-MMS22-SQL19
- - name: artifactName
+ - name: abstractionsArtifactName
type: string
- default: Artifacts
+ default: Abstractions.Artifact
+
+ - name: mdsArtifactName
+ type: string
+ default: MDS.Artifact
- name: platform
type: string
@@ -30,8 +45,15 @@ parameters:
type: stepList
default: []
+ - name: abstractionsPackageVersion
+ type: string
+
+ - name: mdsPackageVersion
+ type: string
+
jobs:
-- job: build_nugets
+- job: build_mds_akv_packages_job
+ displayName: Build MDS & AKV Packages
pool:
name: ${{parameters.poolName }}
@@ -46,36 +68,65 @@ jobs:
- ${{ if ne(parameters.prebuildSteps, '') }}:
- ${{ parameters.prebuildSteps }} # extra steps to run before the build like downloading sni and the required configuration
+ # If we're testing in Package mode, download the Abstractions package artifacts into packages/,
+ # and then setup the top-level NuGet.config to look in packages/ for local
+ # NuGet dependencies.
+ - ${{ if eq(parameters.referenceType, 'Package') }}:
+ - task: DownloadPipelineArtifact@2
+ displayName: Download Abstractions Package Artifact
+ inputs:
+ artifactName: ${{ parameters.abstractionsArtifactName }}
+ targetPath: $(packagePath)
+ # Note that packages/ will have been created by the above step, which is a
+ # pre-requisite for configuring NuGet.
+ - template: ../steps/ci-prebuild-step.yml@self
+ parameters:
+ debug: ${{ parameters.debug }}
+ referenceType: ${{ parameters.referenceType }}
+
- template: ../steps/ci-project-build-step.yml@self
parameters:
platform: ${{ parameters.platform }}
buildConfiguration: ${{ parameters.buildConfiguration }}
+ referenceType: ${{ parameters.referenceType }}
operatingSystem: Windows
- build: all
+ build: MDS
+ abstractionsPackageVersion: ${{parameters.abstractionsPackageVersion}}
- template: ../steps/generate-nuget-package-step.yml@self
parameters:
- NugetPackageVersion: $(NugetPackageVersion)
buildConfiguration: ${{ parameters.buildConfiguration }}
- nuspecPath: 'tools/specs/Microsoft.Data.SqlClient.nuspec'
- OutputDirectory: $(packagePath)
+ displayName: 'Create MDS NuGet Package'
generateSymbolsPackage: false
- properties: 'AbstractionsPackageVersion=$(abstractionsPackageVersion)'
- displayName: 'Generate NuGet package M.D.SqlClient'
+ packageVersion: ${{ parameters.mdsPackageVersion }}
+ nuspecPath: 'tools/specs/Microsoft.Data.SqlClient.nuspec'
+ outputDirectory: $(packagePath)
+ properties: 'AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }}'
+ referenceType: ${{ parameters.referenceType }}
+
+ - template: ../steps/ci-project-build-step.yml@self
+ parameters:
+ platform: ${{ parameters.platform }}
+ buildConfiguration: ${{ parameters.buildConfiguration }}
+ referenceType: ${{ parameters.referenceType }}
+ operatingSystem: Windows
+ build: AKV
+ mdsPackageVersion: ${{parameters.mdsPackageVersion}}
- template: ../steps/generate-nuget-package-step.yml@self
parameters:
- NugetPackageVersion: $(NugetPackageVersion)
buildConfiguration: ${{ parameters.buildConfiguration }}
- nuspecPath: 'tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec'
- OutputDirectory: $(packagePath)
+ displayName: 'Create AKV NuGet Package'
generateSymbolsPackage: false
- properties: 'MdsPackageVersion=$(mdsPackageVersion)'
installNuget: false
- displayName: 'Generate NuGet package AKV Provider'
+ nuspecPath: 'tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec'
+ outputDirectory: $(packagePath)
+ packageVersion: $(akvPackageVersion)
+ properties: 'MdsPackageVersion=${{ parameters.mdsPackageVersion }}'
+ referenceType: ${{ parameters.referenceType }}
- - task: PublishBuildArtifacts@1
- displayName: 'Publish Artifact: Artifacts'
+ - task: PublishPipelineArtifact@1
+ displayName: 'Publish Pipeline Artifact'
inputs:
- PathtoPublish: $(packagePath)
- ArtifactName: ${{ parameters.artifactName }}
+ targetPath: $(packagePath)
+ artifactName: ${{ parameters.mdsArtifactName }}
diff --git a/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml b/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml
index a2b7bdc2fa..c4b64cec96 100644
--- a/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml
+++ b/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml
@@ -4,12 +4,31 @@
# See the LICENSE file in the project root for more information. #
#################################################################################
parameters:
+ - name: abstractionsArtifactName
+ type: string
+
+ - name: abstractionsPackageVersion
+ type: string
+
+ - name: configProperties
+ type: object
+ default: {} # - key: 'value'
+
+ - name: configSqlFor
+ type: string # local, azure, or enclave
+ default: local
+
- name: debug
type: boolean
default: false
- - name: poolName
- type: string
+ - name: enableX64Test
+ type: boolean
+ default: true
+
+ - name: enableX86Test
+ type: boolean
+ default: false
- name: hostedPool
type: boolean
@@ -21,46 +40,14 @@ parameters:
- name: jobDisplayName
type: string
- - name: usemanagedSNI
- type: boolean
- default: false
-
- - name: configProperties
- type: object
- default: {} # - key: 'value'
-
- - name: prebuildSteps
- type: stepList
- default: []
-
- - name: artifactName
+ - name: mdsArtifactName
type: string
- default: Artifacts
- - name: targetFramework
+ - name: mdsPackageVersion
type: string
- name: netcoreVersionTestUtils
type: string
-
- - name: enableX86Test
- type: boolean
- default: false
-
- - name: enableX64Test
- type: boolean
- default: true
-
- - name: testSet
- type: string
-
- - name: publishTestResults
- type: boolean
- default: false
-
- - name: configSqlFor
- type: string # local, azure, or enclave
- default: local
- name: operatingSystem
type: string
@@ -71,17 +58,38 @@ parameters:
values:
- Debug
- Release
+
+ - name: poolName
+ type: string
+
+ - name: prebuildSteps
+ type: stepList
+ default: []
- - name: buildType
- default: Project
+ - name: publishTestResults
+ type: boolean
+ default: false
+
+ - name: referenceType
+ type: string
values:
- Project
- Package
+ - name: targetFramework
+ type: string
+
+ - name: testSet
+ type: string
+
# The timeout, in minutes, for this job.
- name: timeout
type: number
+ - name: usemanagedSNI
+ type: boolean
+ default: false
+
jobs:
- job: ${{ format('{0}', coalesce(parameters.jobDisplayName, parameters.image, 'unknown_image')) }}
@@ -102,6 +110,22 @@ jobs:
value: '$(dotnetx86Path)'
steps:
+
+ # If we're testing in Package mode, download the Abstractions and MDS package
+ # artifacts and put them in the packages/ directory in the repo root.
+ - ${{ if eq(parameters.referenceType, 'Package') }}:
+ - task: DownloadPipelineArtifact@2
+ displayName: Download Abstractions Package Artifact
+ inputs:
+ artifactName: ${{ parameters.abstractionsArtifactName }}
+ targetPath: $(Build.SourcesDirectory)/packages
+
+ - task: DownloadPipelineArtifact@2
+ displayName: Download MDS Package Artifact
+ inputs:
+ artifactName: ${{ parameters.mdsArtifactName }}
+ targetPath: $(Build.SourcesDirectory)/packages
+
- ${{ if ne(parameters.prebuildSteps, '') }}:
- ${{ parameters.prebuildSteps }} # extra steps to run before the build like downloading sni and the required configuration
@@ -232,8 +256,10 @@ jobs:
parameters:
targetFramework: ${{ parameters.targetFramework }}
buildConfiguration: ${{ parameters.buildConfiguration }}
- referenceType: ${{ parameters.buildType }}
+ referenceType: ${{ parameters.referenceType }}
testSet: ${{ parameters.testSet }}
+ abstractionsPackageVersion: ${{ parameters.abstractionsPackageVersion }}
+ mdsPackageVersion: ${{ parameters.mdsPackageVersion }}
${{ if ne(parameters.operatingSystem, 'Windows') }}:
OSGroup: Unix
@@ -243,7 +269,7 @@ jobs:
debug: ${{ parameters.debug }}
targetFramework: ${{ parameters.targetFramework }}
buildConfiguration: ${{ parameters.buildConfiguration }}
- referenceType: ${{ parameters.buildType }}
+ referenceType: ${{ parameters.referenceType }}
testSet: ${{ parameters.testSet }}
operatingSystem: ${{ parameters.operatingSystem }}
@@ -286,13 +312,13 @@ jobs:
debug: ${{ parameters.debug }}
targetFramework: ${{ parameters.targetFramework }}
buildConfiguration: ${{ parameters.buildConfiguration }}
- referenceType: ${{ parameters.buildType }}
+ referenceType: ${{ parameters.referenceType }}
testSet: ${{ parameters.testSet }}
msbuildArchitecture: x86
dotnetx86RootPath: $(dotnetx86RootPath)
operatingSystem: ${{ parameters.operatingSystem }}
- - ${{ if and(eq(parameters.publishTestResults, true), eq(parameters.buildType, 'Project')) }}: # publish test results if build type is project
+ - ${{ if and(eq(parameters.publishTestResults, true), eq(parameters.referenceType, 'Project')) }}: # publish test results if build type is project
- template: ../steps/publish-test-results-step.yml@self
parameters:
debug: ${{ parameters.debug }}
diff --git a/eng/pipelines/common/templates/jobs/run-tests-package-reference-job.yml b/eng/pipelines/common/templates/jobs/run-tests-package-reference-job.yml
index 59700587f7..61cb1db200 100644
--- a/eng/pipelines/common/templates/jobs/run-tests-package-reference-job.yml
+++ b/eng/pipelines/common/templates/jobs/run-tests-package-reference-job.yml
@@ -50,9 +50,7 @@ jobs:
- template: ../steps/update-nuget-config-local-feed-step.yml
parameters:
- downloadedNugetPath: $(Pipeline.Workspace)\${{parameters.packageFolderName }}
- ${{ if parameters.isPreview }}:
- nugetPackageVersion: $(PreviewNugetPackageVersion)
+ packagePath: $(Pipeline.Workspace)\${{parameters.packageFolderName }}
- template: ../steps/update-config-file-step.yml
parameters:
@@ -68,7 +66,7 @@ jobs:
referenceType: Package
buildConfiguration: Release
${{ if parameters.isPreview }}:
- nugetPackageVersion: $(PreviewNugetPackageVersion)
+ mdsPackageVersion: $(previewMdsPackageVersion)
- template: ../steps/build-and-run-tests-netcore-step.yml
parameters:
@@ -76,4 +74,4 @@ jobs:
buildConfiguration: Release
cleanFirst: true
${{ if parameters.isPreview }}:
- nugetPackageVersion: $(PreviewNugetPackageVersion)
+ mdsPackageVersion: $(previewMdsPackageVersion)
diff --git a/eng/pipelines/common/templates/jobs/validate-signed-package-job.yml b/eng/pipelines/common/templates/jobs/validate-signed-package-job.yml
index 009e6f2647..42000da697 100644
--- a/eng/pipelines/common/templates/jobs/validate-signed-package-job.yml
+++ b/eng/pipelines/common/templates/jobs/validate-signed-package-job.yml
@@ -25,14 +25,6 @@ parameters:
- pdb
- both
- - name: assembly_file_version_netfx
- type: string
- default: $(AssemblyFileVersion)
-
- - name: assembly_file_version_core
- type: string
- default: $(AssemblyFileVersion)
-
- name: isPreview
type: boolean
@@ -53,19 +45,14 @@ jobs:
- name: pathToDownloadedNuget # path to the downloaded nuget files
value: $(Pipeline.Workspace)\${{parameters.packageFolderName }}
- - name: ProductVersion #MDS product version (MDS validation)
- value: $(NugetPackageVersion)
-
- name: BuildType
value: $[ stageDependencies.buildMDS.build_signed_package.outputs['GetBuildType.CDP_BUILD_TYPE_COPY'] ]
- ${{ if parameters.isPreview }}:
- name: extractedNugetPath
- value: $(extractedNugetRootPath).$(PreviewNugetPackageVersion)
- - name: NugetPackageVersion
- value: $(PreviewNugetPackageVersion)
- - name: ProductVersion
- value: $(PreviewNugetPackageVersion)
+ value: $(extractedNugetRootPath).$(previewMdsPackageVersion)
+ - name: mdsPackageVersion
+ value: $(previewMdsPackageVersion)
steps:
- script: SET
@@ -75,7 +62,7 @@ jobs:
displayName: 'Use NuGet'
- powershell: |
- #Sets Variables for AssemblyFileVersion, AssemblyVersion and NugetPackageVersion
+ # Sets the pipeline ASSEMBLY_VERSION variable.
[Xml] $versionprops = Get-Content -Path ".\tools\props\Versions.props"
Write-Host $versionprops.Project.PropertyGroup[0].AssemblyFileVersion
@@ -283,60 +270,47 @@ jobs:
displayName: 'Verify all dlls status are Valid'
- powershell: |
- # This will check for ProductVersion and FileVersion.
- #
- # For NetFx we have a different FileVersion, but product versions are all
- # the same. Some may have extra numbering at the end. We only check for
- # the first parts.
+ # This will check each DLL's ProductVersion and FileVersion against
+ # expected values.
+ $failed = 0
foreach ( $pVersion in Get-ChildItem *.dll -Path $(extractedNugetPath) -Recurse | ForEach-Object versioninfo )
{
- if ($pVersion.ProductVersion -Like '$(ProductVersion)*')
+ if ($pVersion.ProductVersion -Like '$(mdsPackageVersion)*')
{
- Write-Host Valid Product Version:"$pVersion.ProductVersion" $pVersion.ProductVersion detected for $pVersion.FileName -ForegroundColor Green
+ Write-Host -ForegroundColor Green "Correct ProductVersion detected for $($pVersion.FileName): $($pVersion.ProductVersion)"
}
else
{
- Write-Host "Wrong ProductVersion detected. Expected: '$(ProductVersion)', but Detected: "$pVersion.ProductVersion""
- Exit -1
+ Write-Host -ForegroundColor Red "Wrong ProductVersion detected for $($pVersion.FileName); expected: $(mdsPackageVersion); found: $($pVersion.ProductVersion)"
+ $failed = 1
}
- if($pVersion.FileName -like '*lib\$(CurrentNetFxVersion)*'){
-
- if($pVersion.FileVersion -eq '${{parameters.assembly_file_version_netfx }}')
- {
- Write-Host 'Correct File version Detected for net46,' $pVersion.FileVersion -ForegroundColor Green
- }
- else
- {
- Write-Host 'Wrong File version Detected for net46,' $pVersion.FileVersion -ForegroundColor Red
- Exit -1
- }
+ if ($pVersion.FileVersion -eq '$(mdsAssemblyFileVersion)')
+ {
+ Write-Host -ForegroundColor Green "Correct FileVersion detected for $($pVersion.FileName): $($pVersion.FileVersion)"
}
else
{
-
- if($pVersion.FileVersion -eq '${{parameters.assembly_file_version_core}}')
- {
- Write-Host 'Correct File version Detected for netcore,' $pVersion.FileVersion -ForegroundColor Green
- }
- else
- {
- Write-Host 'Wrong File version Detected for netcore and ref folder,' $pVersion.FileVersion -ForegroundColor Red
- Exit -1
- }
+ Write-Host -ForegroundColor Red "Wrong FileVersion detected for $($pVersion.FileName); expected $(mdsAssemblyFileVersion); found: $($pVersion.FileVersion)"
+ $failed = 1
}
}
- Get-ChildItem *.dll -Path $(extractedNugetPath) -Recurse | ForEach-Object versioninfo
- displayName: 'Verify "File Version" matches provided pipeline variable "ASSEMBLY_FILE_VERSION" for DLLs'
+ if ($failed -ne 0)
+ {
+ Exit -1
+ }
+
+ Get-ChildItem *.dll -Path $(extractedNugetPath) -Recurse | ForEach-Object VersionInfo | Format-List
+ displayName: 'Verify "File Version" matches expected values for DLLs'
- powershell: |
# Change TestMicrosoftDataSqlClientVersion
[Xml] $versionprops = Get-Content -Path "tools/props/Versions.props"
$versionpropspath = "tools\props\Versions.props"
- $versionprops.Project.PropertyGroup[$versionprops.Project.PropertyGroup.Count-1].TestMicrosoftDataSqlClientVersion ="$(NugetPackageVersion)"
+ $versionprops.Project.PropertyGroup[$versionprops.Project.PropertyGroup.Count-1].TestMicrosoftDataSqlClientVersion ="$(mdsPackageVersion)"
Write-Host "Saving Test nuget version at $rootfolder\props ...." -ForegroundColor Green
$versionprops.Save($versionpropspath)
@@ -344,10 +318,16 @@ jobs:
- powershell: |
# Check assembly versions.
+ #
+ # GOTCHA: This expects the Versions.props file having XML elements in a
+ # certain order. If the order changes, this check will fail!
+ #
+ # TODO: This also isn't checking the versions of the actual assemblies in
+ # the package, so it isn't terribly useful.
[Xml] $versionprops = Get-Content -Path "tools/props/Versions.props"
- $AssemblyFileVersion = $versionprops.Project.PropertyGroup[0].AssemblyFileVersion
- $AssemblyVersion = $versionprops.Project.PropertyGroup[0].AssemblyVersion
+ $AssemblyFileVersion = $versionprops.Project.PropertyGroup[2].AssemblyFileVersion
+ $AssemblyVersion = $versionprops.Project.PropertyGroup[2].AssemblyVersion
if($AssemblyFileVersion -eq $AssemblyVersion)
{
diff --git a/eng/pipelines/common/templates/stages/ci-run-tests-stage.yml b/eng/pipelines/common/templates/stages/ci-run-tests-stage.yml
index 363535aa7e..77cc320af0 100644
--- a/eng/pipelines/common/templates/stages/ci-run-tests-stage.yml
+++ b/eng/pipelines/common/templates/stages/ci-run-tests-stage.yml
@@ -4,22 +4,11 @@
# See the LICENSE file in the project root for more information. #
#################################################################################
parameters:
- - name: debug
- type: boolean
- default: false
-
- - name: testConfigurations
- type: object
-
- - name: dependsOn
+ - name: abstractionsArtifactName
type: string
- default: ''
- - name: buildType
- default: Project
- values:
- - Project
- - Package
+ - name: abstractionsPackageVersion
+ type: string
- name: buildConfiguration
type: string
@@ -27,14 +16,38 @@ parameters:
- Debug
- Release
- - name: prebuildSteps
- type: stepList
+ - name: debug
+ type: boolean
+ default: false
+
+ - name: dependsOn
+ type: object
default: []
+
+ - name: mdsArtifactName
+ type: string
+ default: MDS.Artifact
+
+ - name: mdsPackageVersion
+ type: string
- name: postTestJobs
type: jobList
default: []
+ - name: prebuildSteps
+ type: stepList
+ default: []
+
+ - name: referenceType
+ default: Project
+ values:
+ - Project
+ - Package
+
+ - name: testConfigurations
+ type: object
+
# The timeout, in minutes, for each test job.
- name: testJobTimeout
type: number
@@ -43,10 +56,7 @@ stages:
- ${{ each config in parameters.testConfigurations }}:
- ${{ each image in config.value.images }}:
- stage: ${{ image.key }}
- ${{ if ne(parameters.dependsOn, '') }}:
- dependsOn: ${{ parameters.dependsOn }}
- ${{ else }}:
- dependsOn: []
+ dependsOn: ${{ parameters.dependsOn }}
jobs:
- ${{ each targetFramework in config.value.TargetFrameworks }}:
- ${{ each platform in config.value.buildPlatforms }}:
@@ -56,13 +66,17 @@ stages:
parameters:
debug: ${{ parameters.debug }}
buildConfiguration: ${{ parameters.buildConfiguration }}
- buildType: ${{ parameters.buildType }}
+ referenceType: ${{ parameters.referenceType }}
timeout: ${{ parameters.testJobTimeout }}
poolName: ${{ config.value.pool }}
hostedPool: ${{ eq(config.value.hostedPool, true) }}
image: ${{ image.value }}
jobDisplayName: ${{ format('{0}_{1}_{2}', replace(targetFramework, '.', '_'), platform, testSet) }}
configProperties: ${{ config.value.configProperties }}
+ abstractionsArtifactName: ${{ parameters.abstractionsArtifactName }}
+ abstractionsPackageVersion: ${{parameters.abstractionsPackageVersion}}
+ mdsArtifactName: ${{ parameters.mdsArtifactName }}
+ mdsPackageVersion: ${{ parameters.mdsPackageVersion }}
prebuildSteps: ${{ parameters.prebuildSteps }}
targetFramework: ${{ targetFramework }}
netcoreVersionTestUtils: ${{config.value.netcoreVersionTestUtils }}
@@ -83,7 +97,7 @@ stages:
parameters:
debug: ${{ parameters.debug }}
buildConfiguration: ${{ parameters.buildConfiguration }}
- buildType: ${{ parameters.buildType }}
+ referenceType: ${{ parameters.referenceType }}
timeout: ${{ parameters.testJobTimeout }}
poolName: ${{ config.value.pool }}
hostedPool: ${{ eq(config.value.hostedPool, true) }}
@@ -94,6 +108,10 @@ stages:
jobDisplayName: ${{ format('{0}_{1}_{2}_{3}', replace(targetFramework, '.', '_'), platform, 'NativeSNI', testSet) }}
configProperties: ${{ config.value.configProperties }}
useManagedSNI: ${{ useManagedSNI }}
+ abstractionsArtifactName: ${{ parameters.abstractionsArtifactName }}
+ abstractionsPackageVersion: ${{parameters.abstractionsPackageVersion}}
+ mdsArtifactName: ${{ parameters.mdsArtifactName }}
+ mdsPackageVersion: ${{ parameters.mdsPackageVersion }}
prebuildSteps: ${{ parameters.prebuildSteps }}
targetFramework: ${{ targetFramework }}
netcoreVersionTestUtils: ${{config.value.netcoreVersionTestUtils }}
diff --git a/eng/pipelines/common/templates/steps/build-all-configurations-signed-dlls-step.yml b/eng/pipelines/common/templates/steps/build-all-configurations-signed-dlls-step.yml
index ef19650643..760024ae9f 100644
--- a/eng/pipelines/common/templates/steps/build-all-configurations-signed-dlls-step.yml
+++ b/eng/pipelines/common/templates/steps/build-all-configurations-signed-dlls-step.yml
@@ -4,9 +4,12 @@
# See the LICENSE file in the project root for more information. #
#################################################################################
parameters:
- - name: AssemblyFileVersion
+
+ - name: abstractionsAssemblyFileVersion
+ type: string
+
+ - name: abstractionsPackageVersion
type: string
- default: $(AssemblyFileVersion)
- name: buildConfiguration
type: string
@@ -14,15 +17,17 @@ parameters:
- Debug
- Release
- - name: packageRefMdsVersion
+ - name: mdsAssemblyFileVersion
type: string
- default: ''
- - name: product
- default: MDS
+ - name: mdsPackageVersion
+ type: string
+
+ - name: referenceType
+ type: string
values:
- - MDS
- - MSS
+ - Package
+ - Project
steps:
- task: DownloadSecureFile@1
@@ -49,10 +54,9 @@ steps:
packageType: runtime
version: '8.x'
-- ${{ if eq(parameters.product, 'MDS') }}:
- - task: MSBuild@1
- displayName: 'BuildAllConfigurations using build.proj'
- inputs:
- solution: '**/build.proj'
- configuration: '${{parameters.buildConfiguration }}'
- msbuildArguments: '-p:AssemblyFileVersion=${{parameters.AssemblyFileVersion }} -t:BuildAllConfigurations -p:GenerateNuget=false -p:SigningKeyPath=$(Agent.TempDirectory)\netfxKeypair.snk'
+- task: MSBuild@1
+ displayName: 'BuildAllConfigurations using build.proj'
+ inputs:
+ solution: '**/build.proj'
+ configuration: '${{ parameters.buildConfiguration }}'
+ msbuildArguments: '-t:BuildAllConfigurations -p:ReferenceType=${{ parameters.referenceType }} -p:GenerateNuget=false -p:SigningKeyPath=$(Agent.TempDirectory)\netfxKeypair.snk -p:MdsPackageVersion=${{ parameters.mdsPackageVersion }} -p:AssemblyFileVersion=${{ parameters.mdsAssemblyFileVersion }} -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }} -p:AbstractionsAssemblyFileVersion=${{ parameters.abstractionsAssemblyFileVersion }}'
diff --git a/eng/pipelines/common/templates/steps/build-all-tests-step.yml b/eng/pipelines/common/templates/steps/build-all-tests-step.yml
index 1074c97632..a83108740c 100644
--- a/eng/pipelines/common/templates/steps/build-all-tests-step.yml
+++ b/eng/pipelines/common/templates/steps/build-all-tests-step.yml
@@ -4,16 +4,8 @@
# See the LICENSE file in the project root for more information. #
#################################################################################
parameters:
- - name: targetFramework
- type: string
-
- - name: nugetPackageVersion
- type: string
- default: $(NugetPackageVersion)
-
- - name: platform
+ - name: abstractionsPackageVersion
type: string
- default: $(Platform)
- name: buildConfiguration
type: string
@@ -21,16 +13,26 @@ parameters:
- Debug
- Release
+ - name: mdsPackageVersion
+ type: string
+
+ - name: osGroup
+ type: string
+ default: ''
+
+ - name: platform
+ type: string
+ default: $(Platform)
+
- name: referenceType
- default: Package
+ type: string
values:
- Project
- Package
-
- - name: OSGroup
- type: string
- default: ''
+ - name: targetFramework
+ type: string
+
- name: testSet
type: string
@@ -42,26 +44,16 @@ steps:
solution: build.proj
platform: '${{parameters.platform }}'
configuration: '${{parameters.buildConfiguration }}'
- msbuildArguments: '-t:BuildTestsNetFx -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.nugetPackageVersion }}'
-
-# - ${{else if contains(parameters.targetFramework, 'netstandard')}}: # .NET Standard
-# - task: MSBuild@1
-# displayName: 'Build Tests NetStandard'
-# inputs:
-# solution: build.proj
-# platform: '${{parameters.platform }}'
-# configuration: '${{parameters.buildConfiguration }}'
-# msbuildArguments: '-t:BuildTestsNetCore -p:ReferenceType=NetStandard -p:TargetNetStandardVersion=${{parameters.targetNetStandardVersion }} -p:TF=${{parameters.targetFramework }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.nugetPackageVersion }}'
-# condition: and(succeeded(), not(startsWith(variables['TF'], 'net4')), startsWith(variables['TargetNetStandardVersion'], 'netstandard'))
+ msbuildArguments: '-t:BuildTestsNetFx -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }}'
-- ${{elseif eq(parameters.OSGroup, '')}}: # .NET on Windows
+- ${{elseif eq(parameters.osGroup, '')}}: # .NET on Windows
- task: MSBuild@1
displayName: 'Build Tests NetCore [Win]'
inputs:
solution: build.proj
platform: '${{parameters.platform }}'
configuration: '${{parameters.buildConfiguration }}'
- msbuildArguments: '-t:BuildTestsNetCore -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.nugetPackageVersion }}'
+ msbuildArguments: '-t:BuildTestsNetCore -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }}'
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
- ${{ else }}: # .NET on Unix
@@ -71,7 +63,7 @@ steps:
command: custom
projects: build.proj
custom: msbuild
- arguments: '-t:BuildTestsNetCore -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.nugetPackageVersion }} -p:OSGroup=${{parameters.OSGroup }} -p:platform=${{parameters.platform }} -p:Configuration=${{parameters.buildConfiguration }}'
+ arguments: '-t:BuildTestsNetCore -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} -p:OSGroup=${{parameters.osGroup }} -p:platform=${{parameters.platform }} -p:Configuration=${{parameters.buildConfiguration }} -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }}'
verbosityRestore: Detailed
verbosityPack: Detailed
condition: and(succeeded(), ne(variables['Agent.OS'], 'Windows_NT'))
diff --git a/eng/pipelines/common/templates/steps/build-and-run-tests-netcore-step.yml b/eng/pipelines/common/templates/steps/build-and-run-tests-netcore-step.yml
index aabbef29eb..3d9ee4cf65 100644
--- a/eng/pipelines/common/templates/steps/build-and-run-tests-netcore-step.yml
+++ b/eng/pipelines/common/templates/steps/build-and-run-tests-netcore-step.yml
@@ -20,9 +20,9 @@ parameters:
- Project
- Package
- - name: NugetPackageVersion
+ - name: mdsPackageVersion
type: string
- default: $(NugetPackageVersion)
+ default: $(mdsPackageVersion)
- name: platform
type: string
@@ -57,14 +57,14 @@ steps:
inputs:
solution: build.proj
msbuildArchitecture: x64
- msbuildArguments: '-p:Configuration=${{parameters.buildConfiguration }} -t:BuildAKVNetCore -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.NugetPackageVersion }}'
+ msbuildArguments: '-p:Configuration=${{parameters.buildConfiguration }} -t:BuildAKVNetCore -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }}'
- task: MSBuild@1
displayName: 'MSBuild Build Tests for ${{parameters.TargetNetCoreVersion }}'
inputs:
solution: build.proj
msbuildArchitecture: x64
- msbuildArguments: '-t:BuildTestsNetCore -p:ReferenceType=${{parameters.referenceType }} -p:TargetNetCoreVersion=${{parameters.TargetNetCoreVersion }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.NugetPackageVersion }} -p:Configuration=${{parameters.buildConfiguration }}'
+ msbuildArguments: '-t:BuildTestsNetCore -p:ReferenceType=${{parameters.referenceType }} -p:TargetNetCoreVersion=${{parameters.TargetNetCoreVersion }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} -p:Configuration=${{parameters.buildConfiguration }}'
# Don't run unit tests using package reference. Unit tests are only run using project reference.
@@ -73,12 +73,12 @@ steps:
inputs:
command: test
projects: 'src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.FunctionalTests.csproj'
- arguments: '-p:Platform=${{parameters.platform }} -p:TestTargetOS="${{parameters.TestTargetOS }}" -p:TargetNetCoreVersion=${{parameters.TargetNetCoreVersion }} -p:ReferenceType=${{parameters.referenceType }} -p:Configuration=${{parameters.buildConfiguration }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.NugetPackageVersion }} --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests"'
+ arguments: '-p:Platform=${{parameters.platform }} -p:TestTargetOS="${{parameters.TestTargetOS }}" -p:TargetNetCoreVersion=${{parameters.TargetNetCoreVersion }} -p:ReferenceType=${{parameters.referenceType }} -p:Configuration=${{parameters.buildConfiguration }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests"'
- task: DotNetCoreCLI@2
displayName: 'Run Manual Tests for ${{parameters.TargetNetCoreVersion }}'
inputs:
command: test
projects: 'src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj'
- arguments: '-p:Platform=${{parameters.platform }} -p:TestTargetOS="${{parameters.TestTargetOS }}" -p:TargetNetCoreVersion=${{parameters.TargetNetCoreVersion }} -p:ReferenceType=${{parameters.referenceType }} -p:Configuration=${{parameters.buildConfiguration }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.NugetPackageVersion }} --no-build -v n --filter category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests --collect "Code Coverage"'
+ arguments: '-p:Platform=${{parameters.platform }} -p:TestTargetOS="${{parameters.TestTargetOS }}" -p:TargetNetCoreVersion=${{parameters.TargetNetCoreVersion }} -p:ReferenceType=${{parameters.referenceType }} -p:Configuration=${{parameters.buildConfiguration }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} --no-build -v n --filter category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests --collect "Code Coverage"'
retryCountOnTaskFailure: ${{parameters.retryCountOnManualTests }}
diff --git a/eng/pipelines/common/templates/steps/build-and-run-tests-netfx-step.yml b/eng/pipelines/common/templates/steps/build-and-run-tests-netfx-step.yml
index 20d6ee9e5b..b8a24b7083 100644
--- a/eng/pipelines/common/templates/steps/build-and-run-tests-netfx-step.yml
+++ b/eng/pipelines/common/templates/steps/build-and-run-tests-netfx-step.yml
@@ -20,9 +20,9 @@ parameters:
- Project
- Package
- - name: NugetPackageVersion
+ - name: mdsPackageVersion
type: string
- default: $(NugetPackageVersion)
+ default: $(mdsPackageVersion)
- name: platform
type: string
@@ -57,13 +57,13 @@ steps:
inputs:
solution: build.proj
msbuildArchitecture: x64
- msbuildArguments: '-p:Configuration=${{parameters.buildConfiguration }} -t:BuildAKVNetFx -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.NugetPackageVersion }}'
+ msbuildArguments: '-p:Configuration=${{parameters.buildConfiguration }} -t:BuildAKVNetFx -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }}'
- task: MSBuild@1
displayName: 'MSBuild Build Tests for ${{parameters.TargetNetFxVersion }}'
inputs:
solution: build.proj
- msbuildArguments: ' -t:BuildTestsNetFx -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.NugetPackageVersion }} -p:TargetNetFxVersion=${{parameters.TargetNetFxVersion }} -p:Configuration=${{parameters.buildConfiguration }} -p:Platform=${{parameters.platform }}'
+ msbuildArguments: ' -t:BuildTestsNetFx -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} -p:TargetNetFxVersion=${{parameters.TargetNetFxVersion }} -p:Configuration=${{parameters.buildConfiguration }} -p:Platform=${{parameters.platform }}'
# Don't run unit tests using package reference. Unit tests are only run using project reference.
@@ -72,12 +72,12 @@ steps:
inputs:
command: test
projects: 'src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.FunctionalTests.csproj'
- arguments: '-p:Platform=${{parameters.platform }} -p:TestTargetOS="${{parameters.TestTargetOS }}" -p:TargetNetFxVersion=${{parameters.TargetNetFxVersion }} -p:ReferenceType=${{parameters.referenceType }} -p:Configuration=${{parameters.buildConfiguration }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.NugetPackageVersion }} --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" --collect "Code Coverage"'
+ arguments: '-p:Platform=${{parameters.platform }} -p:TestTargetOS="${{parameters.TestTargetOS }}" -p:TargetNetFxVersion=${{parameters.TargetNetFxVersion }} -p:ReferenceType=${{parameters.referenceType }} -p:Configuration=${{parameters.buildConfiguration }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" --collect "Code Coverage"'
- task: DotNetCoreCLI@2
displayName: 'Run Manual Tests for ${{parameters.TargetNetFxVersion }}'
inputs:
command: test
projects: 'src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj'
- arguments: '-p:Platform=${{parameters.platform }} -p:TestTargetOS="${{parameters.TestTargetOS }}" -p:TargetNetFxVersion=${{parameters.TargetNetFxVersion }} -p:ReferenceType=${{parameters.referenceType }} -p:Configuration=${{parameters.buildConfiguration }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.NugetPackageVersion }} --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" --collect "Code Coverage"'
+ arguments: '-p:Platform=${{parameters.platform }} -p:TestTargetOS="${{parameters.TestTargetOS }}" -p:TargetNetFxVersion=${{parameters.TargetNetFxVersion }} -p:ReferenceType=${{parameters.referenceType }} -p:Configuration=${{parameters.buildConfiguration }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" --collect "Code Coverage"'
retryCountOnTaskFailure: ${{parameters.retryCountOnManualTests }}
diff --git a/eng/pipelines/common/templates/steps/ci-prebuild-step.yml b/eng/pipelines/common/templates/steps/ci-prebuild-step.yml
index 4920482059..6762da1829 100644
--- a/eng/pipelines/common/templates/steps/ci-prebuild-step.yml
+++ b/eng/pipelines/common/templates/steps/ci-prebuild-step.yml
@@ -8,18 +8,8 @@ parameters:
type: boolean
default: false
- - name: artifactName
+ - name: referenceType
type: string
- default: Artifacts
-
- - name: buildConfiguration
- type: string
- values:
- - Debug
- - Release
-
- - name: buildType
- default: Project
values:
- Project
- Package
@@ -48,22 +38,8 @@ steps:
Get-ChildItem env: | Sort-Object Name
displayName: 'List Environment Variables [debug]'
-- ${{if eq(parameters.buildType, 'Package')}}:
- - task: DownloadPipelineArtifact@2
- displayName: 'Download NuGet Package'
- inputs:
- buildType: current
- artifact: ${{parameters.artifactName }}
- patterns: '**/*.nupkg'
- targetPath: $(Pipeline.Workspace)/${{parameters.artifactName }}
-
+- ${{if eq(parameters.referenceType, 'Package')}}:
- template: update-nuget-config-local-feed-step.yml@self
parameters:
- downloadedNugetPath: $(Pipeline.Workspace)\${{parameters.artifactName }}
debug: ${{ parameters.debug }}
-
-- ${{ else }}: # project
- - template: ci-project-build-step.yml@self
- parameters:
- build: allNoDocs
- buildConfiguration: ${{ parameters.buildConfiguration }}
+ packagePath: $(Build.SourcesDirectory)/packages
diff --git a/eng/pipelines/common/templates/steps/ci-project-build-step.yml b/eng/pipelines/common/templates/steps/ci-project-build-step.yml
index 2b40aa8216..1424fdf2ea 100644
--- a/eng/pipelines/common/templates/steps/ci-project-build-step.yml
+++ b/eng/pipelines/common/templates/steps/ci-project-build-step.yml
@@ -13,6 +13,12 @@ parameters:
values:
- Debug
- Release
+
+ - name: referenceType
+ type: string
+ values:
+ - Package
+ - Project
- name: buildNumber
type: string
@@ -36,6 +42,16 @@ parameters:
- all
- allNoDocs
+ # Used when MDS is built with ReferenceType = Package.
+ - name: abstractionsPackageVersion
+ type: string
+ default: ''
+
+ # Used when AKV is built with ReferenceType = Package.
+ - name: mdsPackageVersion
+ type: string
+ default: ''
+
steps:
- template: ./ensure-dotnet-version.yml@self
parameters:
@@ -47,32 +63,27 @@ steps:
packageType: 'runtime'
version: '9.0'
-- template: ./ensure-dotnet-version.yml@self
- parameters:
- packageType: 'runtime'
- version: '8.0'
-
- ${{ if or(eq(parameters.operatingSystem, 'Windows'), eq(parameters.operatingSystem, 'deferedToRuntime')) }}:
- ${{ if or(eq(parameters.build, 'MDS'), eq(parameters.build, 'all'), eq(parameters.build, 'allNoDocs')) }}:
- task: MSBuild@1
- displayName: 'Restore nugets [Win]'
+ displayName: 'Restore [Win]'
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
inputs:
solution: build.proj
msbuildArchitecture: x64
- msbuildArguments: '-t:restore'
+ msbuildArguments: '-t:restore -p:ReferenceType=${{ parameters.ReferenceType }} -p:AbstractionsPackageVersion=${{parameters.abstractionsPackageVersion}}'
retryCountOnTaskFailure: 1
- ${{ if eq(parameters.build, 'allNoDocs') }}:
- task: MSBuild@1
- displayName: 'Build Driver [Win]'
+ displayName: 'Build Driver (no docs) [Win]'
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
inputs:
solution: build.proj
msbuildArchitecture: x64
platform: '${{ parameters.platform }}'
configuration: '${{ parameters.buildConfiguration }}'
- msbuildArguments: '-t:BuildAllConfigurations -p:GenerateDocumentationFile=false -p:GenerateNuGet=false -p:BuildNumber=${{ parameters.buildNumber }}'
+ msbuildArguments: '-t:BuildAllConfigurations -p:ReferenceType=${{ parameters.ReferenceType }} -p:GenerateDocumentationFile=false -p:GenerateNuGet=false -p:BuildNumber=${{ parameters.buildNumber }} -p:AbstractionsPackageVersion=${{parameters.abstractionsPackageVersion}}'
clean: true
- ${{ if or(eq(parameters.build, 'MDS'), eq(parameters.build, 'all')) }}:
@@ -84,7 +95,7 @@ steps:
msbuildArchitecture: x64
platform: '${{ parameters.platform }}'
configuration: '${{ parameters.buildConfiguration }}'
- msbuildArguments: '-t:BuildAllConfigurations -p:GenerateNuGet=false -p:BuildNumber=${{ parameters.buildNumber }}'
+ msbuildArguments: '-t:BuildAllConfigurations -p:ReferenceType=${{ parameters.ReferenceType }} -p:GenerateNuGet=false -p:BuildNumber=${{ parameters.buildNumber }} -p:AbstractionsPackageVersion=${{parameters.abstractionsPackageVersion}}'
clean: true
- ${{ if or(eq(parameters.build, 'AKV'), eq(parameters.build, 'all'), eq(parameters.build, 'allNoDocs')) }}:
@@ -96,7 +107,7 @@ steps:
msbuildArchitecture: x64
platform: '${{ parameters.platform }}'
configuration: '${{ parameters.buildConfiguration }}'
- msbuildArguments: '-t:BuildAKVNetFx -p:BuildNumber=${{ parameters.buildNumber }}'
+ msbuildArguments: '-t:BuildAKVNetFx -p:ReferenceType=${{ parameters.ReferenceType }} -p:BuildNumber=${{ parameters.buildNumber }} -p:MdsPackageVersion=${{parameters.mdsPackageVersion}}'
- task: MSBuild@1
displayName: 'Build AKV Provider NetCore All OS [Win]'
@@ -106,17 +117,17 @@ steps:
msbuildArchitecture: x64
platform: '${{ parameters.platform }}'
configuration: '${{ parameters.buildConfiguration }}'
- msbuildArguments: '-t:BuildAKVNetCoreAllOS -p:BuildNumber=${{ parameters.buildNumber }}'
+ msbuildArguments: '-t:BuildAKVNetCoreAllOS -p:ReferenceType=${{ parameters.ReferenceType }} -p:BuildNumber=${{ parameters.buildNumber }} -p:MdsPackageVersion=${{parameters.mdsPackageVersion}}'
- ${{ if or(eq(parameters.operatingSystem, 'Linux'), eq(parameters.operatingSystem, 'MacOS'), eq(parameters.operatingSystem, 'deferedToRuntime')) }}:
- task: DotNetCoreCLI@2
- displayName: 'Build Driver [non-Win]'
+ displayName: 'Build Driver [${{ parameters.operatingSystem }}]'
condition: and(succeeded(), ne(variables['Agent.OS'], 'Windows_NT'))
inputs:
command: custom
projects: build.proj
custom: msbuild
- arguments: '-t:BuildAll -p:TestEnabled=true -p:GenerateDocumentationFile=false -p:configuration=${{ parameters.buildConfiguration }}'
+ arguments: '-t:BuildAll -p:ReferenceType=${{ parameters.ReferenceType }} -p:TestEnabled=true -p:GenerateDocumentationFile=false -p:configuration=${{ parameters.configuration }} -p:AbstractionsPackageVersion=${{parameters.abstractionsPackageVersion}}'
verbosityRestore: Detailed
verbosityPack: Detailed
retryCountOnTaskFailure: 1
diff --git a/eng/pipelines/common/templates/steps/code-analyze-step.yml b/eng/pipelines/common/templates/steps/code-analyze-step.yml
index 9807541a70..bad64f55d3 100644
--- a/eng/pipelines/common/templates/steps/code-analyze-step.yml
+++ b/eng/pipelines/common/templates/steps/code-analyze-step.yml
@@ -3,40 +3,30 @@
# The .NET Foundation licenses this file to you under the MIT license. #
# See the LICENSE file in the project root for more information. #
#################################################################################
-parameters:
- - name: analyzeType
- values:
- - roslyn
- - inspect
- - all
+# This template defines a step to run Roslyn Analyzers on the MDS project build.
+# It uses the RoslynAnalyzers@3 task from the Secure Development Team's SDL
+# extension:
+#
+# https://eng.ms/docs/cloud-ai-platform/devdiv/one-engineering-system-1es/1es-mohanb/security-integration/guardian-wiki/sdl-azdo-extension/roslyn-analyzers-build-task
+#
+# GOTCHA: This step will clobber any existing build output. It should be run
+# _before_ any build steps that perform versioning or signing.
+
+# @TODO: This can probably be made generic and pass in the command lines for msbuild
+# BUT, they should be kept separate by now as we rebuild build.proj in parallel, we won't
+# affect >1 project at a time.
+
+parameters:
- name: sourceRoot
type: string
default: $(REPOROOT)
- - name: packageRefMdsVersion
- type: string
- default: ''
-
- - name: product
- default: MDS
- values:
- - MDS
- - MSS
-
steps:
-- ${{ if or(eq(parameters.analyzeType, 'roslyn'), eq(parameters.analyzeType, 'all')) }}:
- - ${{ if eq(parameters.product, 'MDS') }}:
- - task: securedevelopmentteam.vss-secure-development-tools.build-task-roslynanalyzers.RoslynAnalyzers@3
- displayName: 'Guardian Dotnet Analyzers '
- inputs:
- msBuildVersion: 17.0
- msBuildArchitecture: x64
- setupCommandlinePicker: vs2022
- msBuildCommandline: 'msbuild ${{parameters.sourceRoot}}\build.proj -p:configuration=Release -p:GenerateNuget=false -p:BuildTools=false -p:SigningKeyPath=$(Agent.TempDirectory)\netfxKeypair.snk'
-
-- ${{ if or(eq(parameters.analyzeType, 'inspect'), eq(parameters.analyzeType, 'all')) }}:
- - task: securedevelopmentteam.vss-secure-development-tools.build-task-codeinspector.CodeInspector@2
- displayName: 'Run Code Inspector'
+ - task: securedevelopmentteam.vss-secure-development-tools.build-task-roslynanalyzers.RoslynAnalyzers@3
+ displayName: Roslyn Analyzers
inputs:
- LogLevel: Error
+ msBuildVersion: 17.0
+ msBuildArchitecture: x64
+ setupCommandlinePicker: vs2022
+ msBuildCommandline: 'msbuild ${{parameters.sourceRoot}}\build.proj -p:configuration=Release -p:GenerateNuget=false -p:BuildTools=false -p:SigningKeyPath=$(Agent.TempDirectory)\netfxKeypair.snk'
diff --git a/eng/pipelines/common/templates/steps/copy-dlls-for-test-step.yml b/eng/pipelines/common/templates/steps/copy-dlls-for-test-step.yml
index c33bd36645..711c3a9b8f 100644
--- a/eng/pipelines/common/templates/steps/copy-dlls-for-test-step.yml
+++ b/eng/pipelines/common/templates/steps/copy-dlls-for-test-step.yml
@@ -85,6 +85,6 @@ steps:
$software = '${{parameters.softwareFolder}}'
$symbols = '${{parameters.symbolsFolder}}'
- Get-ChildItem -recurse "$software\*.dll"
- Get-ChildItem -recurse "$symbols\*.pdb"
+ Get-ChildItem -recurse "$software\*.dll" | ForEach-Object VersionInfo | Format-List
+ Get-ChildItem -recurse "$symbols\*.pdb" | ForEach-Object VersionInfo | Format-List
displayName: 'List the prepared files'
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 cc6518bb39..3a57aa93a8 100644
--- a/eng/pipelines/common/templates/steps/generate-nuget-package-step.yml
+++ b/eng/pipelines/common/templates/steps/generate-nuget-package-step.yml
@@ -6,13 +6,11 @@
parameters:
- name: nuspecPath
type: string
- default: '$(nuspecPath)'
- - name: NugetPackageVersion
+ - name: packageVersion
type: string
- default: '$(NugetPackageVersion)'
- - name: OutputDirectory
+ - name: outputDirectory
type: string
default: '$(Build.SourcesDirectory)/packages'
@@ -29,12 +27,17 @@ parameters:
- name: displayName
type: string
- default: 'NuGet pack with snupkg'
- name: installNuget
type: boolean
default: true
+ - name: referenceType
+ type: string
+ values:
+ - Package
+ - Project
+
# Semi-colon separated properties to pass to nuget via the -properties
# argument.
- name: properties
@@ -58,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}}"'
+ arguments: 'pack -Symbols -SymbolPackageFormat snupkg ${{parameters.nuspecPath}} -Version ${{parameters.NugetPackageVersion}} -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}}"'
+ arguments: 'pack ${{parameters.nuspecPath}} -Version ${{parameters.NugetPackageVersion}} -OutputDirectory ${{parameters.OutputDirectory}} -properties "COMMITID=$(CommitHead);Configuration=${{parameters.buildConfiguration}};ReferenceType=${{parameters.referenceType}};${{parameters.properties}}"'
diff --git a/eng/pipelines/common/templates/steps/publish-symbols-step.yml b/eng/pipelines/common/templates/steps/publish-symbols-step.yml
index a3cd272958..3f80ae8d2d 100644
--- a/eng/pipelines/common/templates/steps/publish-symbols-step.yml
+++ b/eng/pipelines/common/templates/steps/publish-symbols-step.yml
@@ -15,7 +15,7 @@ parameters:
- name: symbolsVersion
type: string
- default: '$(NuGetPackageVersion)'
+ default: '$(mdsPackageVersion)'
- name: symbolServer
type: string
diff --git a/eng/pipelines/common/templates/steps/run-all-tests-step.yml b/eng/pipelines/common/templates/steps/run-all-tests-step.yml
index 91f68eaede..ac080c0019 100644
--- a/eng/pipelines/common/templates/steps/run-all-tests-step.yml
+++ b/eng/pipelines/common/templates/steps/run-all-tests-step.yml
@@ -11,9 +11,9 @@ parameters:
- name: targetFramework
type: string
- - name: nugetPackageVersion
+ - name: mdsPackageVersion
type: string
- default: $(NugetPackageVersion)
+ default: $(mdsPackageVersion)
- name: platform
type: string
@@ -64,9 +64,9 @@ steps:
platform: '${{parameters.platform }}'
configuration: '${{parameters.buildConfiguration }}'
${{ if eq(parameters.msbuildArchitecture, 'x64') }}:
- msbuildArguments: '-t:RunUnitTests -p:TF=${{parameters.targetFramework }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.nugetPackageVersion }}'
+ msbuildArguments: '-t:RunUnitTests -p:TF=${{parameters.targetFramework }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }}'
${{ else }}: # x86
- msbuildArguments: '-t:RunUnitTests -p:TF=${{parameters.targetFramework }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.nugetPackageVersion }} -p:DotnetPath=${{parameters.dotnetx86RootPath }}'
+ msbuildArguments: '-t:RunUnitTests -p:TF=${{parameters.targetFramework }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} -p:DotnetPath=${{parameters.dotnetx86RootPath }}'
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
retryCountOnTaskFailure: 1
@@ -78,9 +78,9 @@ steps:
platform: '${{parameters.platform }}'
configuration: '${{parameters.buildConfiguration }}'
${{ if eq(parameters.msbuildArchitecture, 'x64') }}:
- msbuildArguments: '-t:RunFunctionalTests -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.nugetPackageVersion }}'
+ msbuildArguments: '-t:RunFunctionalTests -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }}'
${{ else }}: # x86
- msbuildArguments: '-t:RunFunctionalTests -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.nugetPackageVersion }} -p:DotnetPath=${{parameters.dotnetx86RootPath }}'
+ msbuildArguments: '-t:RunFunctionalTests -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} -p:DotnetPath=${{parameters.dotnetx86RootPath }}'
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
retryCountOnTaskFailure: 1
@@ -92,9 +92,9 @@ steps:
platform: '${{parameters.platform }}'
configuration: '${{parameters.buildConfiguration }}'
${{ if eq(parameters.msbuildArchitecture, 'x64') }}:
- msbuildArguments: '-t:RunManualTests -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.nugetPackageVersion }}'
+ msbuildArguments: '-t:RunManualTests -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }}'
${{ else }}: # x86
- msbuildArguments: '-t:RunManualTests -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.nugetPackageVersion }} -p:DotnetPath=${{parameters.dotnetx86RootPath }}'
+ msbuildArguments: '-t:RunManualTests -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} -p:DotnetPath=${{parameters.dotnetx86RootPath }}'
condition: eq(variables['Agent.OS'], 'Windows_NT')
retryCountOnTaskFailure: 2
@@ -106,7 +106,7 @@ steps:
command: custom
projects: build.proj
custom: msbuild
- arguments: '-t:RunUnitTests -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.nugetPackageVersion }} -p:platform=${{parameters.platform }} -p:Configuration=${{parameters.buildConfiguration }}'
+ arguments: '-t:RunUnitTests -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} -p:platform=${{parameters.platform }} -p:Configuration=${{parameters.buildConfiguration }}'
verbosityRestore: Detailed
verbosityPack: Detailed
retryCountOnTaskFailure: 1
@@ -118,7 +118,7 @@ steps:
command: custom
projects: build.proj
custom: msbuild
- arguments: '-t:RunFunctionalTests -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.nugetPackageVersion }} -p:platform=${{parameters.platform }} -p:Configuration=${{parameters.buildConfiguration }}'
+ arguments: '-t:RunFunctionalTests -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} -p:platform=${{parameters.platform }} -p:Configuration=${{parameters.buildConfiguration }}'
verbosityRestore: Detailed
verbosityPack: Detailed
retryCountOnTaskFailure: 1
@@ -130,7 +130,7 @@ steps:
command: custom
projects: build.proj
custom: msbuild
- arguments: '-t:RunManualTests -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.nugetPackageVersion }} -p:platform=${{parameters.platform }} -p:Configuration=${{parameters.buildConfiguration }}'
+ arguments: '-t:RunManualTests -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} -p:platform=${{parameters.platform }} -p:Configuration=${{parameters.buildConfiguration }}'
verbosityRestore: Detailed
verbosityPack: Detailed
retryCountOnTaskFailure: 2
diff --git a/eng/pipelines/common/templates/steps/update-nuget-config-local-feed-step.yml b/eng/pipelines/common/templates/steps/update-nuget-config-local-feed-step.yml
index 4eac341108..ac2c517aee 100644
--- a/eng/pipelines/common/templates/steps/update-nuget-config-local-feed-step.yml
+++ b/eng/pipelines/common/templates/steps/update-nuget-config-local-feed-step.yml
@@ -8,76 +8,56 @@ parameters:
type: boolean
default: false
- - name: downloadedNugetPath # path to the downloaded nuget files
+ # The path to add as a NuGet source.
+ - name: packagePath
type: string
- - name: nugetPackageVersion
- type: string
- default: $(NugetPackageVersion)
-
steps:
-- powershell: |
+- pwsh: |
+ # Resolve the path to an absolute path.
+ #
+ # Resolve-Path throws an error if the path doesn't exist yet, which is an
+ # expected scenario here, so we must dance around it.
+ $PackagePath = Resolve-Path "${{ parameters.packagePath }}" -ErrorAction SilentlyContinue -ErrorVariable resolveError
+ if (-Not ($PackagePath))
+ {
+ $PackagePath = $resolveError[0].TargetObject
+ }
+
+ # Ensure the package path exists, for example if we are going to generate
+ # packages in a later step.
+ if (-Not (Test-Path -Path "$PackagePath"))
+ {
+ New-Item -ItemType Directory -Path "$PackagePath" -Force
+ Write-Host "Created package path: $PackagePath"
+ }
+
+ Write-Host "Adding package path: $PackagePath"
+
# Get a list of package sources available
Get-PackageSource
- #Current location
+ # Current location
Get-Location
# Register the local nuget folder to be used by nuget.config
- Register-PackageSource -Name "Package Source" -Location ${{parameters.downloadedNugetPath }} -Force -ProviderName NuGet -Trusted
+ Register-PackageSource -Name "Pipeline Source" -Location "$PackagePath" -Force -ProviderName NuGet -Trusted
# Get a list of package sources available after the change
Get-PackageSource
- #Set the NuGet.config file in the project to use extracted package
+ # Set the NuGet.config file in the project to use extracted package
$rootFolder = Get-location
[Xml] $nugetConfig = Get-Content -Path "NuGet.config"
- $Value = Resolve-Path ${{parameters.downloadedNugetPath }}
$newAdd = $nugetConfig.CreateElement("add")
- $newAdd.SetAttribute("key","Package source")
- $newAdd.SetAttribute("value", "$Value/" )
+ $newAdd.SetAttribute("key","pipeline_source")
+ $newAdd.SetAttribute("value", "$PackagePath" )
$nugetConfig.configuration.packageSources.AppendChild($newAdd)
$nugetConfig.Save("$rootFolder/NuGet.config")
- displayName: 'Update NuGet config file to read from Nuget folder'
+ displayName: 'Add source to NuGet.config'
- ${{ if parameters.debug }}:
- - powershell: |
+ - pwsh: |
# Display the content of the NuGet.config file
Get-Content -Path "NuGet.config"
displayName: 'Read NuGet.config [debug]'
-
-- task: DotNetCoreCLI@2
- displayName: 'Restore NuGets'
- inputs:
- command: 'custom'
- custom: 'msbuild'
- arguments: 'build.proj -t:restore'
- feedsToUse: 'select'
-
-- powershell: |
- $Doc = [xml](Get-Content "./Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj")
- $parent_xpath = '/Project/ItemGroup/ProjectReference'
- $node = $Doc.SelectSingleNode($parent_xpath)
- $parentNode = $node.ParentNode
- while($node -ne $null) {
- $node.ParentNode.RemoveChild($node)
- $node = $Doc.SelectSingleNode($parent_xpath)
- }
-
- $parent_xpath = '/Project/ItemGroup/PackageReference[@Include="Microsoft.Data.SqlClient"]'
- $node = $Doc.SelectSingleNode($parent_xpath)
-
- if($node -eq $null){
- $packagerefnode = $doc.createelement("packagereference")
- $value = $doc.selectsinglenode('/project/itemgroup/projectreference')
- $attrinclude = $doc.createattribute("include")
- $attrinclude.value = "microsoft.data.sqlclient"
- $packagerefnode.attributes.append($attrinclude)
- $parentNode.AppendChild($packageRefNode)
- }
-
- $currentFolder = Get-Location
- $filePath = Join-Path $currentFolder "Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj"
- $Doc.Save($filePath)
- workingDirectory: 'src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider'
- displayName: 'Update AKV Project Ref to Package Ref (.NET Framework/Core)'
diff --git a/eng/pipelines/dotnet-sqlclient-ci-core.yml b/eng/pipelines/dotnet-sqlclient-ci-core.yml
index f94432880f..71025ac738 100644
--- a/eng/pipelines/dotnet-sqlclient-ci-core.yml
+++ b/eng/pipelines/dotnet-sqlclient-ci-core.yml
@@ -68,9 +68,12 @@ parameters:
type: object
default: [net462, net8.0, net9.0, net10.0]
-- name: buildType
- displayName: 'Build Type'
- default: Project
+# The way we will reference sibling projects in the .csproj files:
+# Project - use references.
+# Package - use references to NuGet packages in the
+# packages/ directory.
+- name: referenceType
+ displayName: 'Reference Type'
values:
- Project
- Package
@@ -103,17 +106,45 @@ parameters:
variables:
- template: libraries/ci-build-variables.yml@self
- - name: artifactName
- value: Artifacts
+ - name: abstractionsArtifactName
+ value: Abstractions.Artifact
+
+ - name: mdsArtifactName
+ value: MDS.Artifact
stages:
- - stage: build_nugets
- displayName: 'Build NuGet Packages'
+
+ # Build the Abstractions package, and publish it to the pipeline artifacts
+ # under the given artifact name.
+ - template: stages/build-abstractions-package-ci-stage.yml@self
+ parameters:
+ buildConfiguration: Release
+ abstractionsPackageVersion: $(abstractionsPackageVersion)
+ artifactName: $(abstractionsArtifactName)
+ ${{if eq(parameters.debug, 'true')}}:
+ verbosity: diagnostic
+
+ # Build MDS and its NuGet packages.
+ - stage: build_mds_akv_packages_stage
+ displayName: 'Build MDS & AKV Packages'
+
+ # When building MDS via packages, we must depend on the Abstractions
+ # package.
+ ${{ if eq(parameters.referenceType, 'Package') }}:
+ dependsOn:
+ - build_abstractions_package_stage
+ ${{ else }}:
+ dependsOn: []
+
jobs:
- template: common/templates/jobs/ci-build-nugets-job.yml@self
parameters:
buildConfiguration: ${{ parameters.buildConfiguration }}
- artifactName: $(artifactName)
+ referenceType: ${{ parameters.referenceType }}
+ abstractionsPackageVersion: $(abstractionsPackageVersion)
+ abstractionsArtifactName: $(abstractionsArtifactName)
+ mdsPackageVersion: $(mdsPackageVersion)
+ mdsArtifactName: $(mdsArtifactName)
${{if ne(parameters.SNIVersion, '')}}:
prebuildSteps:
- template: common/templates/steps/override-sni-version.yml@self
@@ -121,46 +152,50 @@ stages:
SNIVersion: ${{parameters.SNIVersion}}
SNIValidationFeed: ${{parameters.SNIValidationFeed}}
+ # 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_nugets]
+ dependsOn: [build_mds_akv_packages_stage]
pipelineArtifactName: $(artifactName)
- mdsPackageVersion: $(NugetPackageVersion)
+ mdsPackageVersion: $(mdsPackageVersion)
${{ if eq(parameters.debug, 'true') }}:
verbosity: 'detailed'
-
+
+ # Run the MDS and AKV tests.
- template: common/templates/stages/ci-run-tests-stage.yml@self
parameters:
debug: ${{ parameters.debug }}
buildConfiguration: ${{ parameters.buildConfiguration }}
- buildType: ${{ parameters.buildType }}
+ referenceType: ${{ parameters.referenceType }}
+ abstractionsArtifactName: $(abstractionsArtifactName)
+ abstractionsPackageVersion: $(abstractionsPackageVersion)
+ mdsArtifactName: $(mdsArtifactName)
+ mdsPackageVersion: $(mdsPackageVersion)
testJobTimeout: ${{ parameters.testJobTimeout }}
${{ if eq(parameters.buildType, 'Package') }}:
dependsOn: build_nugets
- ${{if ne(parameters.SNIVersion, '')}}:
- prebuildSteps: # steps to run prior to building and running tests on each job
+ # When testing MDS via packages, we must depend on the Abstractions and
+ # MDS packages.
+ ${{ if eq(parameters.referenceType, 'Package') }}:
+ dependsOn:
+ - build_abstractions_package_stage
+ - build_mds_akv_packages_stage
+
+ prebuildSteps: # steps to run prior to building and running tests on each job
+ - ${{if ne(parameters.SNIVersion, '')}}:
- template: common/templates/steps/override-sni-version.yml@self
parameters:
SNIVersion: ${{parameters.SNIVersion}}
SNIValidationFeed: ${{parameters.SNIValidationFeed}}
- - template: common/templates/steps/ci-prebuild-step.yml@self
- parameters:
- debug: ${{ parameters.debug }}
- artifactName: $(artifactName)
- buildType: ${{ parameters.buildType }}
- buildConfiguration: ${{ parameters.buildConfiguration }}
- ${{else}}:
- prebuildSteps: # steps to run prior to building and running tests on each job
- - template: common/templates/steps/ci-prebuild-step.yml@self
- parameters:
- debug: ${{ parameters.debug }}
- artifactName: $(artifactName)
- buildType: ${{ parameters.buildType }}
- buildConfiguration: ${{ parameters.buildConfiguration }}
-
+
+ - template: common/templates/steps/ci-prebuild-step.yml@self
+ parameters:
+ debug: ${{ parameters.debug }}
+ referenceType: ${{ parameters.referenceType }}
+
# Include the code coverage job if the build type is Project.
${{ if eq(parameters.buildType, 'Project') }}:
# Jobs to run as part of the tests stage, after the tests are done.
@@ -371,9 +406,8 @@ stages:
# 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)
- AADServicePrincipalId: $(AADServicePrincipalId)
- ${{ if eq(variables['system.pullRequest.isFork'], 'False') }}:
AADServicePrincipalSecret: $(AADServicePrincipalSecret)
+ AADServicePrincipalId: $(AADServicePrincipalId)
AzureKeyVaultUrl: $(AzureKeyVaultUrl)
AzureKeyVaultTenantId: $(AzureKeyVaultTenantId)
SupportsIntegratedSecurity: false
@@ -400,9 +434,8 @@ stages:
AADAuthorityURL: $(AADAuthorityURL)
${{ if eq(variables['system.pullRequest.isFork'], 'False') }}:
AADPasswordConnectionString: $(AAD_PASSWORD_CONN_STR_eastus)
- AADServicePrincipalId: $(AADServicePrincipalId)
- ${{ if eq(variables['system.pullRequest.isFork'], 'False') }}:
AADServicePrincipalSecret: $(AADServicePrincipalSecret)
+ AADServicePrincipalId: $(AADServicePrincipalId)
AzureKeyVaultUrl: $(AzureKeyVaultUrl)
AzureKeyVaultTenantId: $(AzureKeyVaultTenantId)
SupportsIntegratedSecurity: false
@@ -410,39 +443,6 @@ stages:
LocalDbAppName: $(LocalDbAppName)
LocalDbSharedInstanceName: $(LocalDbSharedInstanceName)
- # Only run the AE tests if enable and if we have access to the necessary
- # secrets.
- ${{ if and(eq(parameters.runAlwaysEncryptedTests, true), eq(variables['system.pullRequest.isFork'], 'False')) }}:
- windows_enclave_sql:
- pool: ADO-CI-AE-1ES-Pool
- images:
- Win22_Enclave_Sql19: ADO-MMS22-SQL19
- TargetFrameworks: ${{parameters.targetFrameworks }}
- netcoreVersionTestUtils: ${{parameters.netcoreVersionTestUtils }}
- buildPlatforms: ${{parameters.buildPlatforms }}
- testSets: [AE]
- useManagedSNI: ${{parameters.useManagedSNI }}
- codeCovTargetFrameworks: ${{parameters.codeCovTargetFrameworks }}
- configSqlFor: enclave
- operatingSystem: Windows
- configProperties:
- # config.json properties
- TCPConnectionStringHGSVBS: $(SQL_TCP_CONN_STRING_HGSVBS)
- TCPConnectionStringNoneVBS: $(SQL_TCP_CONN_STRING_NoneVBS)
- TCPConnectionStringAASSGX: $(SQL_TCP_CONN_STRING_AASSGX)
- EnclaveEnabled: true
- AADAuthorityURL: $(AADAuthorityURL)
- AADServicePrincipalId: $(AADServicePrincipalId)
- ${{ if eq(variables['system.pullRequest.isFork'], 'False') }}:
- AADServicePrincipalSecret: $(AADServicePrincipalSecret)
- AzureKeyVaultUrl: $(AzureKeyVaultUrl)
- AzureKeyVaultTenantId: $(AzureKeyVaultTenantId)
- SupportsIntegratedSecurity: $(SupportsIntegratedSecurity)
- UserManagedIdentityClientId: $(UserManagedIdentityClientId)
- AliasName: $(SQLAliasName)
- LocalDbAppName: $(LocalDbAppName)
- LocalDbSharedInstanceName: $(LocalDbSharedInstanceName)
-
# self hosted SQL Server on Linux
linux_ub20_22_sql_22:
pool: ${{parameters.defaultPoolName }}
@@ -489,9 +489,8 @@ stages:
AADAuthorityURL: $(AADAuthorityURL)
${{ if eq(variables['system.pullRequest.isFork'], 'False') }}:
AADPasswordConnectionString: $(AAD_PASSWORD_CONN_STR)
- AADServicePrincipalId: $(AADServicePrincipalId)
- ${{ if eq(variables['system.pullRequest.isFork'], 'False') }}:
AADServicePrincipalSecret: $(AADServicePrincipalSecret)
+ AADServicePrincipalId: $(AADServicePrincipalId)
AzureKeyVaultUrl: $(AzureKeyVaultUrl)
AzureKeyVaultTenantId: $(AzureKeyVaultTenantId)
SupportsIntegratedSecurity: false
@@ -499,9 +498,64 @@ stages:
LocalDbAppName: $(LocalDbAppName)
LocalDbSharedInstanceName: $(LocalDbSharedInstanceName)
+ # Self hosted SQL Server on Mac
+ mac_sql_22:
+ pool: $(defaultHostedPoolName)
+ hostedPool: true
+ images:
+ MacOSLatest_Sql22: macos-latest
+ TargetFrameworks: ${{parameters.targetFrameworksLinux }}
+ netcoreVersionTestUtils: ${{parameters.netcoreVersionTestUtils }}
+ buildPlatforms: [AnyCPU]
+ testSets: ${{parameters.testSets }}
+ useManagedSNI: [true]
+ codeCovTargetFrameworks: ${{parameters.codeCovTargetFrameworks }}
+ configSqlFor: local
+ operatingSystem: Mac
+ configProperties:
+ # config.json properties
+ TCPConnectionString: $(SQL_TCP_CONN_STRING)
+ NPConnectionString: $(SQL_NP_CONN_STRING)
+ SupportsIntegratedSecurity: false
+ ManagedIdentitySupported: false
+ LocalDbAppName: $(LocalDbAppName)
+ LocalDbSharedInstanceName: $(LocalDbSharedInstanceName)
+
+ # Enclave tests
+ #
# Only run the AE tests if enable and if we have access to the necessary
# secrets.
${{ if and(eq(parameters.runAlwaysEncryptedTests, true), eq(variables['system.pullRequest.isFork'], 'False')) }}:
+ windows_enclave_sql:
+ pool: ADO-CI-AE-1ES-Pool
+ images:
+ Win22_Enclave_Sql19: ADO-MMS22-SQL19
+ TargetFrameworks: ${{parameters.targetFrameworks }}
+ netcoreVersionTestUtils: ${{parameters.netcoreVersionTestUtils }}
+ buildPlatforms: ${{parameters.buildPlatforms }}
+ testSets: [AE]
+ useManagedSNI: ${{parameters.useManagedSNI }}
+ codeCovTargetFrameworks: ${{parameters.codeCovTargetFrameworks }}
+ configSqlFor: enclave
+ operatingSystem: Windows
+ configProperties:
+ # config.json properties
+ TCPConnectionStringHGSVBS: $(SQL_TCP_CONN_STRING_HGSVBS)
+ TCPConnectionStringNoneVBS: $(SQL_TCP_CONN_STRING_NoneVBS)
+ TCPConnectionStringAASSGX: $(SQL_TCP_CONN_STRING_AASSGX)
+ EnclaveEnabled: true
+ AADAuthorityURL: $(AADAuthorityURL)
+ AADServicePrincipalId: $(AADServicePrincipalId)
+ ${{ if eq(variables['system.pullRequest.isFork'], 'False') }}:
+ AADServicePrincipalSecret: $(AADServicePrincipalSecret)
+ AzureKeyVaultUrl: $(AzureKeyVaultUrl)
+ AzureKeyVaultTenantId: $(AzureKeyVaultTenantId)
+ SupportsIntegratedSecurity: $(SupportsIntegratedSecurity)
+ UserManagedIdentityClientId: $(UserManagedIdentityClientId)
+ AliasName: $(SQLAliasName)
+ LocalDbAppName: $(LocalDbAppName)
+ LocalDbSharedInstanceName: $(LocalDbSharedInstanceName)
+
linux_enclave_sql:
pool: ADO-CI-AE-1ES-Pool
images:
diff --git a/eng/pipelines/dotnet-sqlclient-ci-package-reference-pipeline.yml b/eng/pipelines/dotnet-sqlclient-ci-package-reference-pipeline.yml
index 6c2f4ea169..88c3abfc01 100644
--- a/eng/pipelines/dotnet-sqlclient-ci-package-reference-pipeline.yml
+++ b/eng/pipelines/dotnet-sqlclient-ci-package-reference-pipeline.yml
@@ -173,7 +173,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/dotnet-sqlclient-ci-project-reference-pipeline.yml b/eng/pipelines/dotnet-sqlclient-ci-project-reference-pipeline.yml
index d83e65dcb0..65104e6961 100644
--- a/eng/pipelines/dotnet-sqlclient-ci-project-reference-pipeline.yml
+++ b/eng/pipelines/dotnet-sqlclient-ci-project-reference-pipeline.yml
@@ -173,7 +173,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/dotnet-sqlclient-signing-pipeline.yml b/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml
index abf2d148d9..a05cc5401f 100644
--- a/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml
+++ b/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml
@@ -8,17 +8,20 @@ name: $(Year:YY)$(DayOfYear)$(Rev:.r)
trigger:
branches:
include:
+
+ # This pipeline is intended to only run against the ADO dotnet-sqlclient
+ # repo.
- internal/main
paths:
include:
+ - .azuredevops
+ - .config
- src
- eng
- tools
- - .config
+ - azurepipelines-coverage.yml
- build.proj
- - Nuget.config
- - '*.cmd'
- - '*.sh'
+ - NuGet.config
schedules:
- cron: '30 4 * * Mon'
@@ -107,14 +110,14 @@ extends:
softwareFolder: $(softwareFolder)
symbolsFolder: $(symbolsFolder)
softwarename: Microsoft.Data.SqlClient
- versionNumber: $(AssemblyFileVersion)
+ versionNumber: $(mdsAssemblyFileVersion)
codeql:
compiled:
enabled: ${{ not(parameters['isPreview']) }}
sbom:
enabled: ${{ not(parameters['isPreview']) }}
packageName: Microsoft.Data.SqlClient
- packageVersion: $(NugetPackageVersion)
+ packageVersion: $(mdsPackageVersion)
policheck:
enabled: ${{ not(parameters['isPreview']) }}
break: true # always break the build on policheck issues. You can disable it by setting to 'false'
diff --git a/eng/pipelines/jobs/build-akv-official-job.yml b/eng/pipelines/jobs/build-akv-official-job.yml
index 5ce8376845..c72e4ad51c 100644
--- a/eng/pipelines/jobs/build-akv-official-job.yml
+++ b/eng/pipelines/jobs/build-akv-official-job.yml
@@ -5,14 +5,17 @@
#################################################################################
parameters:
+ - name: akvAssemblyFileVersion
+ type: string
+
+ - name: akvPackageVersion
+ type: string
+
- name: apiScanDllPath
type: string
- name: apiScanPdbPath
type: string
-
- - name: assemblyFileVersion
- type: string
- name: buildConfiguration
type: string
@@ -20,9 +23,6 @@ parameters:
- Debug
- Release
- - name: nugetPackageVersion
- type: string
-
- name: mdsPackageVersion
type: string
@@ -89,9 +89,18 @@ jobs:
$jsonParams | ConvertFrom-Json | Format-List
displayName: 'Output Job Parameters'
+ # Perform analysis before building, since this step will clobber build
+ # output
+ - template: ../steps/roslyn-analyzers-akv-step.yml@self
+ parameters:
+ akvPackageVersion: '${{ parameters.akvPackageVersion }}'
+ buildConfiguration: '${{ parameters.buildConfiguration }}'
+ mdsPackageVersion: '${{ parameters.mdsPackageVersion }}'
+
- template: ../steps/compound-build-akv-step.yml@self
parameters:
- assemblyFileVersion: '${{ parameters.assemblyFileVersion }}'
+ akvAssemblyFileVersion: '${{ parameters.akvAssemblyFileVersion }}'
+ akvPackageVersion: '${{ parameters.akvPackageVersion }}'
buildConfiguration: '${{ parameters.buildConfiguration }}'
mdsPackageVersion: '${{ parameters.mdsPackageVersion }}'
@@ -104,11 +113,6 @@ jobs:
referenceType: Package
targetFramework: '${{ targetFramework }}'
- - template: ../steps/roslyn-analyzers-akv-step.yml@self
- parameters:
- buildConfiguration: '${{ parameters.buildConfiguration }}'
- mdsPackageVersion: '${{ parameters.mdsPackageVersion }}'
-
- template: ../steps/compound-esrp-code-signing-step.yml@self
parameters:
appRegistrationClientId: '${{ parameters.signingAppRegistrationClientId }}'
@@ -123,10 +127,11 @@ jobs:
parameters:
buildConfiguration: '${{ parameters.buildConfiguration }}'
generateSymbolsPackage: true # Always generate symbols, even if they are not published
- packageVersion: '${{ parameters.nugetPackageVersion }}'
+ packageVersion: '${{ parameters.akvPackageVersion }}'
nuspecPath: '$(REPO_ROOT)/tools/specs/add-ons/$(PACKAGE_NAME).nuspec'
outputDirectory: '$(ARTIFACT_PATH)'
- referenceType: 'Package'
+ referenceType: Package
+ properties: MdsPackageVersion=${{ parameters.mdsPackageVersion }}
- template: ../steps/compound-esrp-code-signing-step.yml@self
parameters:
@@ -141,7 +146,7 @@ jobs:
- ${{ if parameters.publishSymbols }}:
- template: ../steps/compound-publish-symbols-step.yml@self
parameters:
- artifactName: 'akv_symbols_$(System.TeamProject)_$(Build.Repository.Name)_$(Build.SourceBranchName)_${{ parameters.nugetPackageVersion }}_$(System.TimelineId)'
+ artifactName: 'akv_symbols_$(System.TeamProject)_$(Build.Repository.Name)_$(Build.SourceBranchName)_${{ parameters.akvPackageVersion }}_$(System.TimelineId)'
azureSubscription: '${{ parameters.symbolsAzureSubscription }}'
publishProjectName: '${{ parameters.symbolsPublishProjectName }}'
packageName: '$(PACKAGE_NAME)'
@@ -154,4 +159,4 @@ jobs:
Windows_NT/${{ parameters.buildConfiguration }}.AnyCPU/AzureKeyVaultProvider/**/$(PACKAGE_NAME).pdb
AnyOS/${{ parameters.buildConfiguration }}.AnyCPU/AzureKeyVaultProvider/**/$(PACKAGE_NAME).pdb
uploadAccount: '${{ parameters.symbolsUploadAccount }}'
- version: '${{ parameters.nugetPackageVersion }}'
+ version: '${{ parameters.akvPackageVersion }}'
diff --git a/eng/pipelines/jobs/pack-abstractions-package-ci-job.yml b/eng/pipelines/jobs/pack-abstractions-package-ci-job.yml
new file mode 100644
index 0000000000..13bf3618fb
--- /dev/null
+++ b/eng/pipelines/jobs/pack-abstractions-package-ci-job.yml
@@ -0,0 +1,148 @@
+################################################################################
+# 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.
+################################################################################
+
+# This job packs the Abstractions package into NuGet and symbols packages and
+# publishes them as a named pipeline artifact.
+#
+# This template defines a job named 'pack_abstractions_package_job' that can be
+# depended on by downstream jobs.
+
+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
+ type: string
+ default: Abstractions.Artifact
+
+ # The type of build to test (Release or Debug)
+ - name: buildConfiguration
+ type: string
+ values:
+ - Release
+ - Debug
+
+ # The list of upstream jobs to depend on.
+ - name: dependsOn
+ type: object
+ default: []
+
+ # The verbosity level for the dotnet CLI commands.
+ - name: verbosity
+ type: string
+ default: normal
+ values:
+ - quiet
+ - minimal
+ - normal
+ - detailed
+ - diagnostic
+
+jobs:
+
+ - job: pack_abstractions_package_job
+ displayName: 'Pack Abstractions Package'
+
+ dependsOn: ${{ parameters.dependsOn }}
+
+ pool:
+ name: Azure Pipelines
+ vmImage: ubuntu-latest
+
+ variables:
+
+ # The Abstractions project file to use for all dotnet CLI commands.
+ - name: project
+ value: src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Abstractions.csproj
+
+ # The directory where the NuGet packages will be staged before being
+ # published as pipeline artifacts.
+ - name: dotnetPackagesDir
+ value: $(Build.StagingDirectory)/dotnetPackages
+
+ # dotnet CLI arguments common to all commands.
+ - name: commonArguments
+ value: >-
+ --verbosity ${{ parameters.verbosity }}
+
+ # dotnet CLI arguments for build/test/pack commands
+ - name: buildArguments
+ value: >-
+ $(commonArguments)
+ --configuration ${{ parameters.buildConfiguration }}
+ -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }}
+
+ # Explicitly unset the $PLATFORM environment variable that is set by the
+ # 'ADO Build properties' Library in the ADO SqlClientDrivers public project.
+ # This is defined with a non-standard Platform of 'AnyCPU', and will fail
+ # the builds if left defined. The stress tests solution does not require
+ # any specific Platform, and so its solution file doesn't support any
+ # non-standard platforms.
+ #
+ # Note that Azure Pipelines will inject this variable as PLATFORM into the
+ # environment of all tasks in this job.
+ #
+ # See:
+ # https://learn.microsoft.com/en-us/azure/devops/pipelines/process/variables?view=azure-devops&tabs=yaml%2Cbatch
+ #
+ - name: Platform
+ value: ''
+
+ # Do the same for $CONFIGURATION since we explicitly set it using our
+ # 'buildConfiguration' parameter, and we don't want the environment to
+ # override us.
+ - name: Configuration
+ value: ''
+
+ steps:
+
+ # Install the .NET 9.0 SDK.
+ - task: UseDotNet@2
+ displayName: Install .NET 9.0 SDK
+ inputs:
+ packageType: sdk
+ version: 9.x
+
+ # 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.
+ - task: DotNetCoreCLI@2
+ displayName: Restore Solution
+ inputs:
+ command: custom
+ custom: restore
+ projects: $(project)
+ arguments: $(commonArguments)
+
+ # Build the solution.
+ - task: DotNetCoreCLI@2
+ displayName: Build Solution
+ inputs:
+ command: custom
+ custom: build
+ projects: $(project)
+ arguments: $(buildArguments) --no-restore
+
+ # Create the NuGet packages.
+ - task: DotNetCoreCLI@2
+ displayName: Create NuGet Package
+ inputs:
+ command: custom
+ custom: pack
+ projects: $(project)
+ arguments: $(buildArguments) --no-build -o $(dotnetPackagesDir)
+
+ # Publish the NuGet packages as a named pipeline artifact.
+ - task: PublishPipelineArtifact@1
+ displayName: Publish Pipeline Artifact
+ inputs:
+ targetPath: $(dotnetPackagesDir)
+ artifactName: ${{ parameters.artifactName }}
+ publishLocation: pipeline
diff --git a/eng/pipelines/jobs/stress-tests-ci-job.yml b/eng/pipelines/jobs/stress-tests-ci-job.yml
index 2e01470fe5..cc2913c367 100644
--- a/eng/pipelines/jobs/stress-tests-ci-job.yml
+++ b/eng/pipelines/jobs/stress-tests-ci-job.yml
@@ -4,7 +4,7 @@
# file in the project root for more information.
################################################################################
-# This stage builds and runs stress tests against an MDS NuGet package available
+# This job builds and runs stress tests against an MDS NuGet package available
# as a pipeline artifact.
#
# The stress tests are located here:
diff --git a/eng/pipelines/jobs/test-abstractions-package-ci-job.yml b/eng/pipelines/jobs/test-abstractions-package-ci-job.yml
new file mode 100644
index 0000000000..a5e0f98978
--- /dev/null
+++ b/eng/pipelines/jobs/test-abstractions-package-ci-job.yml
@@ -0,0 +1,174 @@
+################################################################################
+# 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.
+################################################################################
+
+# This job builds the Abstractions package and runs its tests for a set of .NET
+# runtimes.
+#
+# This template defines a job named 'test_abstractions_package_job_'
+# that can be depended on by downstream jobs.
+
+parameters:
+
+ # The type of build to test (Release or Debug)
+ - name: buildConfiguration
+ type: string
+ values:
+ - Release
+ - Debug
+
+ # The prefix to prepend to the job's display name:
+ #
+ # [] Run Stress Tests
+ #
+ - name: displayNamePrefix
+ type: string
+
+ # The suffix to append to the job name.
+ - name: jobNameSuffix
+ type: string
+
+ # The list of .NET Framework runtimes to test against.
+ - name: netFrameworkRuntimes
+ type: object
+ default: []
+
+ # The list of .NET runtimes to test against.
+ - name: netRuntimes
+ type: object
+ default: []
+
+ # The name of the Azure Pipelines pool to use.
+ - 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
+
+jobs:
+
+ - job: test_abstractions_package_job_${{ parameters.jobNameSuffix }}
+ displayName: '[${{ parameters.displayNamePrefix }}] Test Abstractions Package'
+ pool:
+ name: ${{ parameters.poolName }}
+
+ # Images provided by Azure Pipelines must be selected using 'vmImage'.
+ ${{ if eq(parameters.poolName, 'Azure Pipelines') }}:
+ vmImage: ${{ parameters.vmImage }}
+ # Images provided by 1ES must be selected using a demand.
+ ${{ else }}:
+ demands:
+ - imageOverride -equals ${{ parameters.vmImage }}
+
+ variables:
+
+ # The Abstractions test project file to use for all dotnet CLI commands.
+ - name: project
+ value: src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/Abstractions.Test.csproj
+
+ # dotnet CLI arguments common to all commands.
+ - name: commonArguments
+ value: >-
+ --verbosity ${{ parameters.verbosity }}
+
+ # dotnet CLI arguments for build/test/pack commands
+ - name: buildArguments
+ value: >-
+ $(commonArguments)
+ --configuration ${{ parameters.buildConfiguration }}
+
+ # Explicitly unset the $PLATFORM environment variable that is set by the
+ # 'ADO Build properties' Library in the ADO SqlClientDrivers public project.
+ # This is defined with a non-standard Platform of 'AnyCPU', and will fail
+ # the builds if left defined. The stress tests solution does not require
+ # any specific Platform, and so its solution file doesn't support any
+ # non-standard platforms.
+ #
+ # Note that Azure Pipelines will inject this variable as PLATFORM into the
+ # environment of all tasks in this job.
+ #
+ # See:
+ # https://learn.microsoft.com/en-us/azure/devops/pipelines/process/variables?view=azure-devops&tabs=yaml%2Cbatch
+ #
+ - name: Platform
+ value: ''
+
+ # Do the same for $CONFIGURATION since we explicitly set it using our
+ # 'buildConfiguration' parameter, and we don't want the environment to
+ # override us.
+ - name: Configuration
+ value: ''
+
+ steps:
+
+ # Install the .NET 9.0 SDK.
+ - task: UseDotNet@2
+ displayName: Install .NET 9.0 SDK
+ inputs:
+ packageType: sdk
+ version: 9.x
+
+ # Install the .NET 8.0 runtime.
+ - task: UseDotNet@2
+ displayName: Install .NET 8.0 Runtime
+ inputs:
+ packageType: runtime
+ version: 8.x
+
+ # The Windows agent images include a suitable .NET Framework runtime, so
+ # we don't have to install one explicitly.
+
+ # 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.
+ - task: DotNetCoreCLI@2
+ displayName: Restore Solution
+ inputs:
+ command: custom
+ custom: restore
+ projects: $(project)
+ arguments: $(commonArguments)
+
+ # Build the solution.
+ - task: DotNetCoreCLI@2
+ displayName: Build Solution
+ inputs:
+ command: custom
+ custom: build
+ projects: $(project)
+ arguments: $(buildArguments) --no-restore
+
+ # Run the tests for each .NET runtime.
+ - ${{ each runtime in parameters.netRuntimes }}:
+ - task: DotNetCoreCLI@2
+ displayName: Test [${{ runtime }}]
+ inputs:
+ command: custom
+ custom: test
+ projects: $(project)
+ arguments: $(buildArguments) --no-build -f ${{ runtime }}
+
+ # Run the tests for each .NET Framework runtime.
+ - ${{ each runtime in parameters.netFrameworkRuntimes }}:
+ - task: DotNetCoreCLI@2
+ displayName: Test [${{ runtime }}]
+ inputs:
+ command: custom
+ custom: test
+ projects: $(project)
+ arguments: $(buildArguments) --no-build -f ${{ runtime }}
diff --git a/eng/pipelines/libraries/ci-build-variables.yml b/eng/pipelines/libraries/ci-build-variables.yml
index dc5c1b8020..1a83c24cd8 100644
--- a/eng/pipelines/libraries/ci-build-variables.yml
+++ b/eng/pipelines/libraries/ci-build-variables.yml
@@ -6,28 +6,21 @@
variables:
- group: 'ADO Build properties'
- - group: 'ADO CI Packaging'
- group: 'ADO Test Configuration Properties'
- name: buildNumber
value: '$(Build.BuildNumber)'
- name: SQLTarget
value: 'localhost'
- - name: NugetPackageVersion
- value: $(Major).$(Minor)$(Patch)-pull.1$(buildnumber)
+ - name: abstractionsPackageVersion
+ value: 1.0.0.$(buildNumber)-ci
+ - name: akvPackageVersion
+ value: 7.0.0.$(buildNumber)-ci
+ - name: mdsPackageVersion
+ value: 7.0.0.$(buildNumber)-ci
- name: skipComponentGovernanceDetection
value: true
- name: runCodesignValidationInjection
value: false
- name: packagePath
value: '$(Build.SourcesDirectory)/packages'
-
- # TODO(ADO-38703): Remove these when the other pipeline changes arrive.
- - name: baseBuildNumber
- value: $[ split(variables['Build.BuildNumber'], '.')[0] ]
- - name: abstractionsPackageVersion
- value: 1.0.0.$(baseBuildNumber)
- - name: mdsPackageVersion
- value: $(NugetPackageVersion)
- - name: akvPackageVersion
- value: $(NugetPackageVersion)
diff --git a/eng/pipelines/libraries/common-variables.yml b/eng/pipelines/libraries/common-variables.yml
index 9eeedbff32..7ced0c28d2 100644
--- a/eng/pipelines/libraries/common-variables.yml
+++ b/eng/pipelines/libraries/common-variables.yml
@@ -24,6 +24,39 @@ variables:
- name: artifactDirectory
value: '$(REPOROOT)/packages'
+ # C# assembly versions must be in the format: Major.Minor.Build.Revision, but
+ # $(Build.BuildNumber) has the format XXX.YY. Additionally, each version part
+ # must be a positive 16-bit integer less than 65535. Simply concatenating
+ # both parts of $(Build.BuildNumber) could produce values larger than 65534,
+ # so we must omit the second part entirely. Unfortunately, this may result
+ # in multiple subsequent pipline builds using the same C# assembly versions.
+ # The package versions will not be affected and will show the complete
+ # $(Build.BuildNumber) values.
+ - name: assemblyBuildNumber
+ value: $[ split(variables['Build.BuildNumber'], '.')[0] ]
+
+ # ----------------------------------------------------------------------------
+ # Abstractions Package Versions
+ #
+ # These are version values that will be used by the official build. They
+ # should be updated after each release to reflect the next release's versions.
+
+ # The NuGet package version for GA releases (non-preview).
+ - name: abstractionsPackageVersion
+ value: '1.0.0'
+
+ # The NuGet package version for preview releases.
+ - name: abstractionsPackagePreviewVersion
+ value: 1.0.0-preview1.$(Build.BuildNumber)
+
+ # The AssemblyFileVersion for all assemblies in the Abstractions package.
+ #
+ - name: abstractionsAssemblyFileVersion
+ value: 1.0.0.$(assemblyBuildNumber)
+
+ # ----------------------------------------------------------------------------
+ # MDS Package Versions
+
# Update this after every release. This is used to generate the MDS NuGet package version.
- name: Major
value: '7'
@@ -38,21 +71,11 @@ variables:
- name: Revision
value: '3'
- - name: NugetPackageVersion
+ - name: mdsPackageVersion
value: $(Major).$(Minor).$(Patch)
- - name: PreviewNugetPackageVersion
+ - name: previewMdsPackageVersion
value: $(Major).$(Minor).$(Patch)$(Preview)$(Revision).$(Build.BuildNumber)
- - name: AssemblyFileVersion
- value: '$(Major).$(Minor)$(Patch).$(Build.BuildNumber)'
+ - name: mdsAssemblyFileVersion
+ value: $(Major).$(Minor).$(Patch).$(assemblyBuildNumber)
- name: nuspecPath
value: '$(REPOROOT)/tools/specs/Microsoft.Data.SqlClient.nuspec'
-
- # TODO(ADO-38703): Remove these when the other pipeline changes arrive.
- - name: baseBuildNumber
- value: $[ split(variables['Build.BuildNumber'], '.')[0] ]
- - name: abstractionsPackageVersion
- value: 1.0.0.$(baseBuildNumber)
- - name: mdsPackageVersion
- value: $(NugetPackageVersion)
- - name: akvPackageVersion
- value: $(NugetPackageVersion)
diff --git a/eng/pipelines/libraries/mds-validation-variables.yml b/eng/pipelines/libraries/mds-validation-variables.yml
index d7723a059f..93dc0804ff 100644
--- a/eng/pipelines/libraries/mds-validation-variables.yml
+++ b/eng/pipelines/libraries/mds-validation-variables.yml
@@ -13,7 +13,7 @@ variables:
- name: extractedNugetRootPath
value: $(Build.SourcesDirectory)\$(TempFolderName)\Microsoft.Data.SqlClient
- name: extractedNugetPath
- value: $(extractedNugetRootPath).$(NugetPackageVersion)
+ value: $(extractedNugetRootPath).$(mdsPackageVersion)
- name: expectedFolderNames
value: lib,ref,runtimes
- name: expectedDotnetVersions
diff --git a/eng/pipelines/stages/build-abstractions-package-ci-stage.yml b/eng/pipelines/stages/build-abstractions-package-ci-stage.yml
new file mode 100644
index 0000000000..2a9268e737
--- /dev/null
+++ b/eng/pipelines/stages/build-abstractions-package-ci-stage.yml
@@ -0,0 +1,119 @@
+################################################################################
+# 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.
+################################################################################
+
+# This stage builds the Abstractions package, runs tests, and publishes the
+# resulting NuGet packages as pipeline artifacts.
+#
+# The NuGet packages have the following properties:
+#
+# Name: Microsoft.Data.SqlClient.Extensions.Abstractions
+# Version: ${{ abstractionsPackageVersion }} (from parameter)
+#
+# The following NuGet packages are published:
+#
+# Microsoft.Data.SqlClient.Extensions.Abstractions..nupkg
+# Microsoft.Data.SqlClient.Extensions.Abstractions..snupkg (symbols)
+#
+# The packages are published to pipeline artifacts with the name specified by
+# the ${{ artifactName }} parameter.
+#
+# This template defines a stage named 'build_abstractions_package_stage' that
+# can be depended on by downstream stages.
+
+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
+ type: string
+ default: Abstractions.Artifact
+
+ # The type of build to produce (Release or Debug)
+ - name: buildConfiguration
+ type: string
+ default: Release
+ values:
+ - Release
+ - Debug
+
+ # The verbosity level for the dotnet CLI commands.
+ - name: verbosity
+ type: string
+ default: normal
+ values:
+ - quiet
+ - minimal
+ - normal
+ - detailed
+ - diagnostic
+
+stages:
+
+ - stage: build_abstractions_package_stage
+ displayName: Build Abstractions Package
+
+ jobs:
+
+ # ------------------------------------------------------------------------
+ # Build and test on Linux.
+
+ - template: ../jobs/test-abstractions-package-ci-job.yml@self
+ parameters:
+ jobNameSuffix: linux
+ displayNamePrefix: Linux
+ 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
+ displayNamePrefix: Win
+ poolName: Azure Pipelines
+ vmImage: windows-latest
+ buildConfiguration: ${{ parameters.buildConfiguration }}
+ netRuntimes: [net8.0, net9.0]
+ netFrameworkRuntimes: [net462, net47, net471, net472, net48, net481]
+ verbosity: ${{ parameters.verbosity }}
+
+ # ------------------------------------------------------------------------
+ # Build and test on macOS.
+
+ - template: ../jobs/test-abstractions-package-ci-job.yml
+ parameters:
+ jobNameSuffix: macos
+ displayNamePrefix: macOS
+ 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 }}
+ abstractionsPackageVersion: ${{ parameters.abstractionsPackageVersion }}
+ verbosity: ${{ parameters.verbosity }}
+ 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
diff --git a/eng/pipelines/steps/compound-build-akv-step.yml b/eng/pipelines/steps/compound-build-akv-step.yml
index 2dd21d8514..c58d3febb9 100644
--- a/eng/pipelines/steps/compound-build-akv-step.yml
+++ b/eng/pipelines/steps/compound-build-akv-step.yml
@@ -7,10 +7,13 @@
# @TODO: This can probably be made generic and pass in the command lines for msbuild
# BUT, they should be kept separate by now as we rebuild build.proj in parallel, we won't
# affect >1 project at a time.
-# @TODO: NugetPackageVersion should not be used for MDS package version
+# @TODO: mdsPackageVersion should not be used for MDS package version
parameters:
- - name: assemblyFileVersion
+ - name: akvAssemblyFileVersion
+ type: string
+
+ - name: akvPackageVersion
type: string
- name: buildConfiguration
@@ -41,12 +44,6 @@ steps:
packageType: 'runtime'
version: '9.x'
- - task: UseDotNet@2
- displayName: 'Install .NET 8.x Runtime'
- inputs:
- packageType: 'runtime'
- version: '8.x'
-
- task: MSBuild@1
displayName: 'Build.proj - BuildAkv'
inputs:
@@ -54,8 +51,9 @@ steps:
configuration: '${{ parameters.buildConfiguration }}'
msbuildArguments: >-
-t:BuildAkv
- -p:AssemblyFileVersion=${{ parameters.assemblyFileVersion }}
- -p:NugetPackageVersion=${{ parameters.mdsPackageVersion }}
+ -p:AssemblyFileVersion=${{ parameters.akvAssemblyFileVersion }}
+ -p:AkvPackageVersion=${{ parameters.akvPackageVersion }}
+ -p:MdsPackageVersion=${{ parameters.mdsPackageVersion }}
-p:ReferenceType=Package
-p:SigningKeyPath=$(Agent.TempDirectory)/netfxKeypair.snk
diff --git a/eng/pipelines/steps/compound-nuget-pack-step.yml b/eng/pipelines/steps/compound-nuget-pack-step.yml
index b07252e29d..41905c8483 100644
--- a/eng/pipelines/steps/compound-nuget-pack-step.yml
+++ b/eng/pipelines/steps/compound-nuget-pack-step.yml
@@ -26,8 +26,14 @@ parameters:
- name: referenceType
type: string
values:
- - Package
- - Project
+ - Package
+ - Project
+
+ # Semi-colon separated properties to pass to nuget via the -properties
+ # argument.
+ - name: properties
+ type: string
+ default: ''
steps:
# This tool is failing on OneBranch pipelines, possibly due to new
@@ -63,7 +69,7 @@ steps:
-SymbolPackageFormat snupkg
-Version ${{ parameters.packageVersion }}
-OutputDirectory ${{ parameters.outputDirectory }}
- -Properties "COMMITID=$(Build.SourceVersion);Configuration=${{ parameters.buildConfiguration }};ReferenceType=${{ parameters.referenceType }}"
+ -Properties "COMMITID=$(Build.SourceVersion);Configuration=${{ parameters.buildConfiguration }};ReferenceType=${{ parameters.referenceType }};${{ parameters.properties }}"
- ${{ else }}:
- task: NuGetCommand@2
displayName: 'Generate NuGet Package'
@@ -74,4 +80,4 @@ steps:
${{ parameters.nuspecPath }}
-Version ${{ parameters.packageVersion }}
-OutputDirectory ${{ parameters.outputDirectory }}
- -Properties "COMMITID=$(Build.SourceVersion);Configuration=${{ parameters.buildConfiguration }};ReferenceType=${{ parameters.referenceType }}"
+ -Properties "COMMITID=$(Build.SourceVersion);Configuration=${{ parameters.buildConfiguration }};ReferenceType=${{ parameters.referenceType }};${{ parameters.properties }}"
diff --git a/eng/pipelines/steps/roslyn-analyzers-akv-step.yml b/eng/pipelines/steps/roslyn-analyzers-akv-step.yml
index ebb6ff0801..8b2b4bd331 100644
--- a/eng/pipelines/steps/roslyn-analyzers-akv-step.yml
+++ b/eng/pipelines/steps/roslyn-analyzers-akv-step.yml
@@ -4,11 +4,23 @@
# See the LICENSE file in the project root for more information. #
#################################################################################
+# This template defines a step to run Roslyn Analyzers on the AKV project build.
+# It uses the RoslynAnalyzers@3 task from the Secure Development Team's SDL
+# extension:
+#
+# https://eng.ms/docs/cloud-ai-platform/devdiv/one-engineering-system-1es/1es-mohanb/security-integration/guardian-wiki/sdl-azdo-extension/roslyn-analyzers-build-task
+#
+# GOTCHA: This step will clobber any existing build output. It should be run
+# _before_ any build steps that perform versioning or signing.
+
# @TODO: This can probably be made generic and pass in the command lines for msbuild
# BUT, they should be kept separate by now as we rebuild build.proj in parallel, we won't
# affect >1 project at a time.
parameters:
+ - name: akvPackageVersion
+ type: string
+
- name: buildConfiguration
type: string
values:
@@ -28,7 +40,8 @@ steps:
$(REPO_ROOT)/build.proj
-t:BuildAkv
-p:Configuration=${{ parameters.buildConfiguration }}
- -p:NugetPackageVersion=${{ parameters.mdsPackageVersion }}
+ -p:AkvPackageVersion=${{ parameters.akvPackageVersion }}
+ -p:MdsPackageVersion=${{ parameters.mdsPackageVersion }}
-p:ReferenceType=Package
msBuildVersion: 17.0
setupCommandLinePicker: vs2022
diff --git a/eng/pipelines/variables/akv-official-variables.yml b/eng/pipelines/variables/akv-official-variables.yml
index aaf8de7c5e..591a562686 100644
--- a/eng/pipelines/variables/akv-official-variables.yml
+++ b/eng/pipelines/variables/akv-official-variables.yml
@@ -35,9 +35,7 @@ variables:
value: '-preview1'
# Compound Variables ---------------------------------------------------
- - name: assemblyFileVersion
+ - name: akvAssemblyFileVersion
value: '${{ variables.versionMajor }}.${{ variables.versionMinor }}${{ variables.versionPatch }}.$(Build.BuildNumber)'
- - name: nugetPackageVersion
+ - name: akvPackageVersion
value: '${{ variables.versionMajor }}.${{ variables.versionMinor }}.${{ variables.versionPatch }}${{ variables.versionPreview }}'
-
-
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 011fdafffb..843f8c0bfe 100644
--- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj
@@ -58,12 +58,9 @@
-
-
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 cddab6fd9f..bf8c916315 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
@@ -1096,12 +1096,9 @@
-
-
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 19be9bb567..6ab452e0c2 100644
--- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj
@@ -58,12 +58,9 @@
-
-
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 ce36beb46e..a4cc2aa55a 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
@@ -1087,12 +1087,9 @@
-
-
diff --git a/tools/props/Versions.props b/tools/props/Versions.props
index 7a3ba3cc9a..d82a04cce2 100644
--- a/tools/props/Versions.props
+++ b/tools/props/Versions.props
@@ -31,10 +31,14 @@
specific to MDS, and then used in the MDS project. As-is, these names are
used by the build tooling and may be unintentionally included in other
(non-MDS) projects.
+
+ For example, the AKV package uses the AssemblyVersion, FileVersion, and
+ Version as its own. It currently isn't possible to build/package both
+ MDS and AKV at the same time.
-->
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
diff --git a/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec b/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec
index a313d0af12..e55fb4e7cb 100644
--- a/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec
+++ b/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec
@@ -36,12 +36,6 @@ Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyStoreProvider.SqlColumnEncrypti
-
-
-
-
-
-
@@ -51,23 +45,28 @@ Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyStoreProvider.SqlColumnEncrypti
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
+
-
-
+
+
+
+
+
+
+
+
+
-
+
-
+
+
@@ -28,7 +41,8 @@
-
+
+
@@ -48,7 +62,8 @@
-
+
+
@@ -62,14 +77,21 @@
-->
+
+
+
+
+
+
+
-
+
@@ -81,11 +103,13 @@
+
+
@@ -97,12 +121,23 @@
-
+
+
+
-
+
+
+
+
+
+
+
+
+
+
diff --git a/NuGet.config.local b/NuGet.config.local
new file mode 100644
index 0000000000..3e23ac372c
--- /dev/null
+++ b/NuGet.config.local
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build.proj b/build.proj
index 7fe296591a..d8343382e3 100644
--- a/build.proj
+++ b/build.proj
@@ -28,10 +28,13 @@
$(TF)
$(TF)
true
- Configuration=$(Configuration);AssemblyVersion=$(SqlServerAssemblyVersion);AssemblyFileVersion=$(SqlServerAssemblyFileVersion);Version=$(SqlServerPackageVersion);
- Configuration=$(Configuration);AssemblyFileVersion=$(AssemblyFileVersion);TargetsWindows=$(TargetsWindows);TargetsUnix=$(TargetsUnix);
+
+ Configuration=$(Configuration);ReferenceType=$(ReferenceType)
+ $(CommonProperties);AssemblyVersion=$(SqlServerAssemblyVersion);AssemblyFileVersion=$(SqlServerAssemblyFileVersion);Version=$(SqlServerPackageVersion);
+ $(CommonProperties);AssemblyFileVersion=$(AssemblyFileVersion);TargetsWindows=$(TargetsWindows);TargetsUnix=$(TargetsUnix);
$(ProjectProperties);BuildForRelease=false;TargetNetCoreVersion=$(TargetNetCoreVersion);TargetNetFxVersion=$(TargetNetFxVersion)
TestResults
+
+
+
+
@@ -101,6 +107,8 @@
+ $(CommonProperties)
+
- AbstractionsPackageVersion=$(AbstractionsPackageVersion)
+ $(AbstractionsProperties);AbstractionsPackageVersion=$(AbstractionsPackageVersion)
@@ -133,7 +141,7 @@
Properties="$(AbstractionsProperties)" />
-
+
-
+
+
+
+ $(CommonProperties)
+
+
+
+ $(AzureProperties);AzurePackageVersion=$(AzurePackageVersion)
+
+
+
+
+ $(AzureProperties);AzureAssemblyFileVersion=$(AzureAssemblyFileVersion)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
@@ -175,7 +239,7 @@
Name="RestoreNetFx"
DependsOnTargets="RestoreSqlServerLib;RestoreAbstractions"
Condition="'$(IsEnabledWindows)' == 'true'">
-
+
-
-
- dotnet build -c Release -p:ReferenceType=$(ReferenceType)
-
-
+
+
+
+
+
+
-
+
-
+
@@ -244,29 +309,29 @@
Name="BuildUnitTestsNetCore"
DependsOnTargets="RestoreTestsNetCore;BuildNetCore"
Condition="$(ReferenceType.Contains('Project'))">
-
+
-
+
-
+
-
+
-
+
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 276893cc4d..939ad9ab0d 100644
--- a/eng/pipelines/common/templates/jobs/build-signed-package-job.yml
+++ b/eng/pipelines/common/templates/jobs/build-signed-package-job.yml
@@ -49,7 +49,7 @@ jobs:
configuration: $(Configuration)
msbuildArguments: -t:BuildTools
- # Perform analysis before building, since this step will clobber build output
+ # Perform analysis before building, since this step will clobber build output.
- template: ../steps/code-analyze-step.yml@self
# Update the root NuGet.config to use the packages/ directory as a source.
diff --git a/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml b/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml
index c4b64cec96..fa95ea52b0 100644
--- a/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml
+++ b/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml
@@ -10,6 +10,12 @@ parameters:
- name: abstractionsPackageVersion
type: string
+ - name: azureArtifactName
+ type: string
+
+ - name: azurePackageVersion
+ type: string
+
- name: configProperties
type: object
default: {} # - key: 'value'
@@ -126,6 +132,12 @@ jobs:
artifactName: ${{ parameters.mdsArtifactName }}
targetPath: $(Build.SourcesDirectory)/packages
+ - task: DownloadPipelineArtifact@2
+ displayName: Download Azure Package Artifact
+ inputs:
+ artifactName: ${{ parameters.azureArtifactName }}
+ targetPath: $(Build.SourcesDirectory)/packages
+
- ${{ if ne(parameters.prebuildSteps, '') }}:
- ${{ parameters.prebuildSteps }} # extra steps to run before the build like downloading sni and the required configuration
@@ -259,6 +271,7 @@ jobs:
referenceType: ${{ parameters.referenceType }}
testSet: ${{ parameters.testSet }}
abstractionsPackageVersion: ${{ parameters.abstractionsPackageVersion }}
+ azurePackageVersion: ${{ parameters.azurePackageVersion }}
mdsPackageVersion: ${{ parameters.mdsPackageVersion }}
${{ if ne(parameters.operatingSystem, 'Windows') }}:
OSGroup: Unix
diff --git a/eng/pipelines/common/templates/jobs/validate-signed-package-job.yml b/eng/pipelines/common/templates/jobs/validate-signed-package-job.yml
index 42000da697..030199e72b 100644
--- a/eng/pipelines/common/templates/jobs/validate-signed-package-job.yml
+++ b/eng/pipelines/common/templates/jobs/validate-signed-package-job.yml
@@ -305,17 +305,6 @@ jobs:
Get-ChildItem *.dll -Path $(extractedNugetPath) -Recurse | ForEach-Object VersionInfo | Format-List
displayName: 'Verify "File Version" matches expected values for DLLs'
- - powershell: |
- # Change TestMicrosoftDataSqlClientVersion
-
- [Xml] $versionprops = Get-Content -Path "tools/props/Versions.props"
- $versionpropspath = "tools\props\Versions.props"
- $versionprops.Project.PropertyGroup[$versionprops.Project.PropertyGroup.Count-1].TestMicrosoftDataSqlClientVersion ="$(mdsPackageVersion)"
- Write-Host "Saving Test nuget version at $rootfolder\props ...." -ForegroundColor Green
- $versionprops.Save($versionpropspath)
-
- displayName: 'Modify TestMicrosoftDataSqlClientVersion'
-
- powershell: |
# Check assembly versions.
#
diff --git a/eng/pipelines/common/templates/stages/ci-run-tests-stage.yml b/eng/pipelines/common/templates/stages/ci-run-tests-stage.yml
index 77cc320af0..6d70f29900 100644
--- a/eng/pipelines/common/templates/stages/ci-run-tests-stage.yml
+++ b/eng/pipelines/common/templates/stages/ci-run-tests-stage.yml
@@ -10,6 +10,12 @@ parameters:
- name: abstractionsPackageVersion
type: string
+ - name: azureArtifactName
+ type: string
+
+ - name: azurePackageVersion
+ type: string
+
- name: buildConfiguration
type: string
values:
@@ -74,7 +80,9 @@ stages:
jobDisplayName: ${{ format('{0}_{1}_{2}', replace(targetFramework, '.', '_'), platform, testSet) }}
configProperties: ${{ config.value.configProperties }}
abstractionsArtifactName: ${{ parameters.abstractionsArtifactName }}
- abstractionsPackageVersion: ${{parameters.abstractionsPackageVersion}}
+ abstractionsPackageVersion: ${{ parameters.abstractionsPackageVersion }}
+ azureArtifactName: ${{ parameters.azureArtifactName }}
+ azurePackageVersion: ${{ parameters.azurePackageVersion }}
mdsArtifactName: ${{ parameters.mdsArtifactName }}
mdsPackageVersion: ${{ parameters.mdsPackageVersion }}
prebuildSteps: ${{ parameters.prebuildSteps }}
@@ -109,7 +117,9 @@ stages:
configProperties: ${{ config.value.configProperties }}
useManagedSNI: ${{ useManagedSNI }}
abstractionsArtifactName: ${{ parameters.abstractionsArtifactName }}
- abstractionsPackageVersion: ${{parameters.abstractionsPackageVersion}}
+ abstractionsPackageVersion: ${{ parameters.abstractionsPackageVersion }}
+ azureArtifactName: ${{ parameters.azureArtifactName }}
+ azurePackageVersion: ${{ parameters.azurePackageVersion }}
mdsArtifactName: ${{ parameters.mdsArtifactName }}
mdsPackageVersion: ${{ parameters.mdsPackageVersion }}
prebuildSteps: ${{ parameters.prebuildSteps }}
diff --git a/eng/pipelines/common/templates/steps/build-all-tests-step.yml b/eng/pipelines/common/templates/steps/build-all-tests-step.yml
index a83108740c..7216451a3d 100644
--- a/eng/pipelines/common/templates/steps/build-all-tests-step.yml
+++ b/eng/pipelines/common/templates/steps/build-all-tests-step.yml
@@ -7,6 +7,9 @@ parameters:
- name: abstractionsPackageVersion
type: string
+ - name: azurePackageVersion
+ type: string
+
- name: buildConfiguration
type: string
values:
@@ -44,7 +47,7 @@ steps:
solution: build.proj
platform: '${{parameters.platform }}'
configuration: '${{parameters.buildConfiguration }}'
- msbuildArguments: '-t:BuildTestsNetFx -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }}'
+ msbuildArguments: '-t:BuildTestsNetFx -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:MdsPackageVersion=${{parameters.mdsPackageVersion }} -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }} -p:AzurePackageVersion=${{ parameters.azurePackageVersion }}'
- ${{elseif eq(parameters.osGroup, '')}}: # .NET on Windows
- task: MSBuild@1
@@ -53,7 +56,7 @@ steps:
solution: build.proj
platform: '${{parameters.platform }}'
configuration: '${{parameters.buildConfiguration }}'
- msbuildArguments: '-t:BuildTestsNetCore -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }}'
+ msbuildArguments: '-t:BuildTestsNetCore -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:MdsPackageVersion=${{parameters.mdsPackageVersion }} -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }} -p:AzurePackageVersion=${{ parameters.azurePackageVersion }}'
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
- ${{ else }}: # .NET on Unix
@@ -63,7 +66,7 @@ steps:
command: custom
projects: build.proj
custom: msbuild
- arguments: '-t:BuildTestsNetCore -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} -p:OSGroup=${{parameters.osGroup }} -p:platform=${{parameters.platform }} -p:Configuration=${{parameters.buildConfiguration }} -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }}'
+ arguments: '-t:BuildTestsNetCore -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:MdsPackageVersion=${{parameters.mdsPackageVersion }} -p:OSGroup=${{parameters.osGroup }} -p:platform=${{parameters.platform }} -p:Configuration=${{parameters.buildConfiguration }} -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }} -p:AzurePackageVersion=${{ parameters.azurePackageVersion }}'
verbosityRestore: Detailed
verbosityPack: Detailed
condition: and(succeeded(), ne(variables['Agent.OS'], 'Windows_NT'))
diff --git a/eng/pipelines/common/templates/steps/build-and-run-tests-netcore-step.yml b/eng/pipelines/common/templates/steps/build-and-run-tests-netcore-step.yml
index 3d9ee4cf65..9114907632 100644
--- a/eng/pipelines/common/templates/steps/build-and-run-tests-netcore-step.yml
+++ b/eng/pipelines/common/templates/steps/build-and-run-tests-netcore-step.yml
@@ -57,14 +57,14 @@ steps:
inputs:
solution: build.proj
msbuildArchitecture: x64
- msbuildArguments: '-p:Configuration=${{parameters.buildConfiguration }} -t:BuildAKVNetCore -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }}'
+ msbuildArguments: '-p:Configuration=${{parameters.buildConfiguration }} -t:BuildAKVNetCore -p:ReferenceType=${{parameters.referenceType }} -p:MdsPackageVersion=${{parameters.mdsPackageVersion }}'
- task: MSBuild@1
displayName: 'MSBuild Build Tests for ${{parameters.TargetNetCoreVersion }}'
inputs:
solution: build.proj
msbuildArchitecture: x64
- msbuildArguments: '-t:BuildTestsNetCore -p:ReferenceType=${{parameters.referenceType }} -p:TargetNetCoreVersion=${{parameters.TargetNetCoreVersion }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} -p:Configuration=${{parameters.buildConfiguration }}'
+ msbuildArguments: '-t:BuildTestsNetCore -p:ReferenceType=${{parameters.referenceType }} -p:TargetNetCoreVersion=${{parameters.TargetNetCoreVersion }} -p:MdsPackageVersion=${{parameters.mdsPackageVersion }} -p:Configuration=${{parameters.buildConfiguration }}'
# Don't run unit tests using package reference. Unit tests are only run using project reference.
@@ -73,12 +73,12 @@ steps:
inputs:
command: test
projects: 'src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.FunctionalTests.csproj'
- arguments: '-p:Platform=${{parameters.platform }} -p:TestTargetOS="${{parameters.TestTargetOS }}" -p:TargetNetCoreVersion=${{parameters.TargetNetCoreVersion }} -p:ReferenceType=${{parameters.referenceType }} -p:Configuration=${{parameters.buildConfiguration }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests"'
+ arguments: '-p:Platform=${{parameters.platform }} -p:TestTargetOS="${{parameters.TestTargetOS }}" -p:TargetNetCoreVersion=${{parameters.TargetNetCoreVersion }} -p:ReferenceType=${{parameters.referenceType }} -p:Configuration=${{parameters.buildConfiguration }} -p:MdsPackageVersion=${{parameters.mdsPackageVersion }} --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests"'
- task: DotNetCoreCLI@2
displayName: 'Run Manual Tests for ${{parameters.TargetNetCoreVersion }}'
inputs:
command: test
projects: 'src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj'
- arguments: '-p:Platform=${{parameters.platform }} -p:TestTargetOS="${{parameters.TestTargetOS }}" -p:TargetNetCoreVersion=${{parameters.TargetNetCoreVersion }} -p:ReferenceType=${{parameters.referenceType }} -p:Configuration=${{parameters.buildConfiguration }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} --no-build -v n --filter category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests --collect "Code Coverage"'
+ arguments: '-p:Platform=${{parameters.platform }} -p:TestTargetOS="${{parameters.TestTargetOS }}" -p:TargetNetCoreVersion=${{parameters.TargetNetCoreVersion }} -p:ReferenceType=${{parameters.referenceType }} -p:Configuration=${{parameters.buildConfiguration }} -p:MdsPackageVersion=${{parameters.mdsPackageVersion }} --no-build -v n --filter category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests --collect "Code Coverage"'
retryCountOnTaskFailure: ${{parameters.retryCountOnManualTests }}
diff --git a/eng/pipelines/common/templates/steps/build-and-run-tests-netfx-step.yml b/eng/pipelines/common/templates/steps/build-and-run-tests-netfx-step.yml
index b8a24b7083..cc75abd154 100644
--- a/eng/pipelines/common/templates/steps/build-and-run-tests-netfx-step.yml
+++ b/eng/pipelines/common/templates/steps/build-and-run-tests-netfx-step.yml
@@ -57,13 +57,13 @@ steps:
inputs:
solution: build.proj
msbuildArchitecture: x64
- msbuildArguments: '-p:Configuration=${{parameters.buildConfiguration }} -t:BuildAKVNetFx -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }}'
+ msbuildArguments: '-p:Configuration=${{parameters.buildConfiguration }} -t:BuildAKVNetFx -p:ReferenceType=${{parameters.referenceType }} -p:MdsPackageVersion=${{parameters.mdsPackageVersion }}'
- task: MSBuild@1
displayName: 'MSBuild Build Tests for ${{parameters.TargetNetFxVersion }}'
inputs:
solution: build.proj
- msbuildArguments: ' -t:BuildTestsNetFx -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} -p:TargetNetFxVersion=${{parameters.TargetNetFxVersion }} -p:Configuration=${{parameters.buildConfiguration }} -p:Platform=${{parameters.platform }}'
+ msbuildArguments: ' -t:BuildTestsNetFx -p:ReferenceType=${{parameters.referenceType }} -p:MdsPackageVersion=${{parameters.mdsPackageVersion }} -p:TargetNetFxVersion=${{parameters.TargetNetFxVersion }} -p:Configuration=${{parameters.buildConfiguration }} -p:Platform=${{parameters.platform }}'
# Don't run unit tests using package reference. Unit tests are only run using project reference.
@@ -72,12 +72,12 @@ steps:
inputs:
command: test
projects: 'src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.FunctionalTests.csproj'
- arguments: '-p:Platform=${{parameters.platform }} -p:TestTargetOS="${{parameters.TestTargetOS }}" -p:TargetNetFxVersion=${{parameters.TargetNetFxVersion }} -p:ReferenceType=${{parameters.referenceType }} -p:Configuration=${{parameters.buildConfiguration }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" --collect "Code Coverage"'
+ arguments: '-p:Platform=${{parameters.platform }} -p:TestTargetOS="${{parameters.TestTargetOS }}" -p:TargetNetFxVersion=${{parameters.TargetNetFxVersion }} -p:ReferenceType=${{parameters.referenceType }} -p:Configuration=${{parameters.buildConfiguration }} -p:MdsPackageVersion=${{parameters.mdsPackageVersion }} --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" --collect "Code Coverage"'
- task: DotNetCoreCLI@2
displayName: 'Run Manual Tests for ${{parameters.TargetNetFxVersion }}'
inputs:
command: test
projects: 'src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj'
- arguments: '-p:Platform=${{parameters.platform }} -p:TestTargetOS="${{parameters.TestTargetOS }}" -p:TargetNetFxVersion=${{parameters.TargetNetFxVersion }} -p:ReferenceType=${{parameters.referenceType }} -p:Configuration=${{parameters.buildConfiguration }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" --collect "Code Coverage"'
+ arguments: '-p:Platform=${{parameters.platform }} -p:TestTargetOS="${{parameters.TestTargetOS }}" -p:TargetNetFxVersion=${{parameters.TargetNetFxVersion }} -p:ReferenceType=${{parameters.referenceType }} -p:Configuration=${{parameters.buildConfiguration }} -p:MdsPackageVersion=${{parameters.mdsPackageVersion }} --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" --collect "Code Coverage"'
retryCountOnTaskFailure: ${{parameters.retryCountOnManualTests }}
diff --git a/eng/pipelines/common/templates/steps/run-all-tests-step.yml b/eng/pipelines/common/templates/steps/run-all-tests-step.yml
index ac080c0019..ae0a3b4364 100644
--- a/eng/pipelines/common/templates/steps/run-all-tests-step.yml
+++ b/eng/pipelines/common/templates/steps/run-all-tests-step.yml
@@ -64,9 +64,9 @@ steps:
platform: '${{parameters.platform }}'
configuration: '${{parameters.buildConfiguration }}'
${{ if eq(parameters.msbuildArchitecture, 'x64') }}:
- msbuildArguments: '-t:RunUnitTests -p:TF=${{parameters.targetFramework }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }}'
+ msbuildArguments: '-t:RunUnitTests -p:TF=${{parameters.targetFramework }} -p:MdsPackageVersion=${{parameters.mdsPackageVersion }}'
${{ else }}: # x86
- msbuildArguments: '-t:RunUnitTests -p:TF=${{parameters.targetFramework }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} -p:DotnetPath=${{parameters.dotnetx86RootPath }}'
+ msbuildArguments: '-t:RunUnitTests -p:TF=${{parameters.targetFramework }} -p:MdsPackageVersion=${{parameters.mdsPackageVersion }} -p:DotnetPath=${{parameters.dotnetx86RootPath }}'
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
retryCountOnTaskFailure: 1
@@ -78,9 +78,9 @@ steps:
platform: '${{parameters.platform }}'
configuration: '${{parameters.buildConfiguration }}'
${{ if eq(parameters.msbuildArchitecture, 'x64') }}:
- msbuildArguments: '-t:RunFunctionalTests -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }}'
+ msbuildArguments: '-t:RunFunctionalTests -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:MdsPackageVersion=${{parameters.mdsPackageVersion }}'
${{ else }}: # x86
- msbuildArguments: '-t:RunFunctionalTests -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} -p:DotnetPath=${{parameters.dotnetx86RootPath }}'
+ msbuildArguments: '-t:RunFunctionalTests -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:MdsPackageVersion=${{parameters.mdsPackageVersion }} -p:DotnetPath=${{parameters.dotnetx86RootPath }}'
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
retryCountOnTaskFailure: 1
@@ -92,9 +92,9 @@ steps:
platform: '${{parameters.platform }}'
configuration: '${{parameters.buildConfiguration }}'
${{ if eq(parameters.msbuildArchitecture, 'x64') }}:
- msbuildArguments: '-t:RunManualTests -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }}'
+ msbuildArguments: '-t:RunManualTests -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:MdsPackageVersion=${{parameters.mdsPackageVersion }}'
${{ else }}: # x86
- msbuildArguments: '-t:RunManualTests -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} -p:DotnetPath=${{parameters.dotnetx86RootPath }}'
+ msbuildArguments: '-t:RunManualTests -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:MdsPackageVersion=${{parameters.mdsPackageVersion }} -p:DotnetPath=${{parameters.dotnetx86RootPath }}'
condition: eq(variables['Agent.OS'], 'Windows_NT')
retryCountOnTaskFailure: 2
@@ -106,7 +106,7 @@ steps:
command: custom
projects: build.proj
custom: msbuild
- arguments: '-t:RunUnitTests -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} -p:platform=${{parameters.platform }} -p:Configuration=${{parameters.buildConfiguration }}'
+ arguments: '-t:RunUnitTests -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:MdsPackageVersion=${{parameters.mdsPackageVersion }} -p:platform=${{parameters.platform }} -p:Configuration=${{parameters.buildConfiguration }}'
verbosityRestore: Detailed
verbosityPack: Detailed
retryCountOnTaskFailure: 1
@@ -118,7 +118,7 @@ steps:
command: custom
projects: build.proj
custom: msbuild
- arguments: '-t:RunFunctionalTests -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} -p:platform=${{parameters.platform }} -p:Configuration=${{parameters.buildConfiguration }}'
+ arguments: '-t:RunFunctionalTests -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:MdsPackageVersion=${{parameters.mdsPackageVersion }} -p:platform=${{parameters.platform }} -p:Configuration=${{parameters.buildConfiguration }}'
verbosityRestore: Detailed
verbosityPack: Detailed
retryCountOnTaskFailure: 1
@@ -130,7 +130,7 @@ steps:
command: custom
projects: build.proj
custom: msbuild
- arguments: '-t:RunManualTests -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:TestMicrosoftDataSqlClientVersion=${{parameters.mdsPackageVersion }} -p:platform=${{parameters.platform }} -p:Configuration=${{parameters.buildConfiguration }}'
+ arguments: '-t:RunManualTests -p:TF=${{parameters.targetFramework }} -p:TestSet=${{parameters.testSet }} -p:ReferenceType=${{parameters.referenceType }} -p:MdsPackageVersion=${{parameters.mdsPackageVersion }} -p:platform=${{parameters.platform }} -p:Configuration=${{parameters.buildConfiguration }}'
verbosityRestore: Detailed
verbosityPack: Detailed
retryCountOnTaskFailure: 2
diff --git a/eng/pipelines/dotnet-sqlclient-ci-core.yml b/eng/pipelines/dotnet-sqlclient-ci-core.yml
index 71025ac738..dd11491fdc 100644
--- a/eng/pipelines/dotnet-sqlclient-ci-core.yml
+++ b/eng/pipelines/dotnet-sqlclient-ci-core.yml
@@ -108,6 +108,9 @@ variables:
- name: abstractionsArtifactName
value: Abstractions.Artifact
+
+ - name: azureArtifactName
+ value: Azure.Artifact
- name: mdsArtifactName
value: MDS.Artifact
@@ -118,7 +121,7 @@ stages:
# under the given artifact name.
- template: stages/build-abstractions-package-ci-stage.yml@self
parameters:
- buildConfiguration: Release
+ buildConfiguration: ${{ parameters.buildConfiguration }}
abstractionsPackageVersion: $(abstractionsPackageVersion)
artifactName: $(abstractionsArtifactName)
${{if eq(parameters.debug, 'true')}}:
@@ -152,6 +155,24 @@ stages:
SNIVersion: ${{parameters.SNIVersion}}
SNIValidationFeed: ${{parameters.SNIValidationFeed}}
+ # Build the Azure package, and publish it to the pipeline artifacts under the
+ # given artifact name.
+ - template: stages/build-azure-package-ci-stage.yml@self
+ parameters:
+ abstractionsArtifactName: $(abstractionsArtifactName)
+ abstractionsPackageVersion: $(abstractionsPackageVersion)
+ azureArtifactName: $(azureArtifactName)
+ azurePackageVersion: $(azurePackageVersion)
+ buildConfiguration: ${{ parameters.buildConfiguration }}
+ # When building via package references, we must depend on the Abstractions
+ # package.
+ ${{ if eq(parameters.referenceType, 'Package') }}:
+ dependsOn:
+ - build_abstractions_package_stage
+ referenceType: ${{ parameters.referenceType }}
+ ${{if eq(parameters.debug, 'true')}}:
+ verbosity: diagnostic
+
# Run the stress tests, if desired.
- ${{ if eq(parameters.enableStressTests, true) }}:
- template: stages/stress-tests-ci-stage.yml@self
@@ -171,17 +192,20 @@ stages:
referenceType: ${{ parameters.referenceType }}
abstractionsArtifactName: $(abstractionsArtifactName)
abstractionsPackageVersion: $(abstractionsPackageVersion)
+ azureArtifactName: $(azureArtifactName)
+ azurePackageVersion: $(azurePackageVersion)
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 and
- # MDS packages.
+ # When testing MDS via packages, we must depend on the Abstractions,
+ # Azure, and MDS packages.
${{ if eq(parameters.referenceType, 'Package') }}:
dependsOn:
- build_abstractions_package_stage
+ - build_azure_package_stage
- build_mds_akv_packages_stage
prebuildSteps: # steps to run prior to building and running tests on each job
diff --git a/eng/pipelines/jobs/build-akv-official-job.yml b/eng/pipelines/jobs/build-akv-official-job.yml
index c72e4ad51c..12909a04e0 100644
--- a/eng/pipelines/jobs/build-akv-official-job.yml
+++ b/eng/pipelines/jobs/build-akv-official-job.yml
@@ -90,7 +90,7 @@ jobs:
displayName: 'Output Job Parameters'
# Perform analysis before building, since this step will clobber build
- # output
+ # output.
- template: ../steps/roslyn-analyzers-akv-step.yml@self
parameters:
akvPackageVersion: '${{ parameters.akvPackageVersion }}'
diff --git a/eng/pipelines/jobs/pack-azure-package-ci-job.yml b/eng/pipelines/jobs/pack-azure-package-ci-job.yml
new file mode 100644
index 0000000000..9cc33eadb1
--- /dev/null
+++ b/eng/pipelines/jobs/pack-azure-package-ci-job.yml
@@ -0,0 +1,188 @@
+################################################################################
+# 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.
+################################################################################
+
+# This job packs the Azure package into NuGet and symbols packages and publishes
+# them as a named pipeline artifact.
+#
+# This template defines a job named 'pack_azure_package_job' that can be
+# depended on by downstream jobs.
+
+parameters:
+
+ # The name of the Abstractions pipeline artifact to download.
+ #
+ # This is used when the referenceType is 'Package'.
+ - name: abstractionsArtifactName
+ type: string
+ default: Abstractions.Artifact
+
+ # The Abstractions package verion to depend on.
+ #
+ # This is used when the referenceType is 'Package'.
+ - name: abstractionsPackageVersion
+ type: string
+
+ # The name of the pipeline artifact to publish.
+ - name: azureArtifactName
+ type: string
+ default: Azure.Artifact
+
+ # The version to apply to the NuGet package and DLLs.
+ - name: azurePackageVersion
+ type: string
+
+ # The type of build to test (Release or Debug)
+ - name: buildConfiguration
+ type: string
+ values:
+ - Release
+ - Debug
+
+ # The list of upstream jobs to depend on.
+ - name: dependsOn
+ type: object
+ default: []
+
+ # The reference type to use:
+ #
+ # Project - dependent projects are referenced directly.
+ # Package - dependent projects are referenced via NuGet packages.
+ #
+ - name: referenceType
+ type: string
+ values:
+ - 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
+ displayName: 'Pack Azure Package'
+
+ dependsOn: ${{ parameters.dependsOn }}
+
+ pool:
+ name: Azure Pipelines
+ vmImage: ubuntu-latest
+
+ variables:
+
+ # The Azure project file to use for all dotnet CLI commands.
+ - name: project
+ value: src/Microsoft.Data.SqlClient.Extensions/Azure/src/Azure.csproj
+
+ # The directory where the NuGet packages will be staged before being
+ # published as pipeline artifacts.
+ - name: dotnetPackagesDir
+ value: $(Build.StagingDirectory)/dotnetPackages
+
+ # dotnet CLI arguments common to all commands.
+ - name: commonArguments
+ value: >-
+ --verbosity ${{ parameters.verbosity }}
+ -p:ReferenceType=${{ parameters.referenceType }}
+ -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }}
+
+ # dotnet CLI arguments for build/test/pack commands
+ - name: buildArguments
+ value: >-
+ $(commonArguments)
+ --configuration ${{ parameters.buildConfiguration }}
+ -p:AzurePackageVersion=${{ parameters.azurePackageVersion }}
+
+ # Explicitly unset the $PLATFORM environment variable that is set by the
+ # 'ADO Build properties' Library in the ADO SqlClientDrivers public
+ # project. This is defined with a non-standard Platform of 'AnyCPU', and
+ # will fail the builds if left defined. The stress tests solution does
+ # not require any specific Platform, and so its solution file doesn't
+ # support any non-standard platforms.
+ #
+ # Note that Azure Pipelines will inject this variable as PLATFORM into the
+ # environment of all tasks in this job.
+ #
+ # See:
+ # https://learn.microsoft.com/en-us/azure/devops/pipelines/process/variables?view=azure-devops&tabs=yaml%2Cbatch
+ #
+ - name: Platform
+ value: ''
+
+ # Do the same for $CONFIGURATION since we explicitly set it using our
+ # 'buildConfiguration' parameter, and we don't want the environment to
+ # override us.
+ - name: Configuration
+ value: ''
+
+ steps:
+
+ # We have a few extra steps for Package reference builds.
+ - ${{ if eq(parameters.referenceType, 'Package') }}:
+
+ # Download the Abstractions package artifacts into packages/.
+ - task: DownloadPipelineArtifact@2
+ displayName: Download Abstractions Package Artifact
+ inputs:
+ artifactName: ${{ parameters.abstractionsArtifactName }}
+ 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
+
+ # Install the .NET 9.0 SDK.
+ - task: UseDotNet@2
+ displayName: Install .NET 9.0 SDK
+ inputs:
+ packageType: sdk
+ version: 9.x
+
+ # 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.
+ - task: DotNetCoreCLI@2
+ displayName: Restore Solution
+ inputs:
+ command: custom
+ custom: restore
+ projects: $(project)
+ arguments: $(commonArguments)
+
+ # Build the solution.
+ - task: DotNetCoreCLI@2
+ displayName: Build Solution
+ inputs:
+ command: custom
+ custom: build
+ projects: $(project)
+ arguments: $(buildArguments) --no-restore
+
+ # Create the NuGet packages.
+ - task: DotNetCoreCLI@2
+ displayName: Create NuGet Package
+ inputs:
+ command: custom
+ custom: pack
+ projects: $(project)
+ arguments: $(buildArguments) --no-build -o $(dotnetPackagesDir)
+
+ # Publish the NuGet packages as a named pipeline artifact.
+ - task: PublishPipelineArtifact@1
+ displayName: Publish Pipeline Artifact
+ inputs:
+ targetPath: $(dotnetPackagesDir)
+ artifactName: ${{ parameters.azureArtifactName }}
+ publishLocation: pipeline
diff --git a/eng/pipelines/jobs/test-azure-package-ci-job.yml b/eng/pipelines/jobs/test-azure-package-ci-job.yml
new file mode 100644
index 0000000000..8e1b8fc6db
--- /dev/null
+++ b/eng/pipelines/jobs/test-azure-package-ci-job.yml
@@ -0,0 +1,214 @@
+################################################################################
+# 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.
+################################################################################
+
+# This job builds the Azure package and runs its tests for a set of .NET
+# runtimes.
+#
+# This template defines a job named 'test_azure_package_job_' that can
+# be depended on by downstream jobs.
+
+parameters:
+
+ # The name of the Abstractions pipeline artifact to download.
+ #
+ # This is used when the referenceType is 'Package'.
+ - name: abstractionsArtifactName
+ type: string
+ default: Abstractions.Artifact
+
+ # The Abstractions package verion to depend on.
+ #
+ # This is used when the referenceType is 'Package'.
+ - name: abstractionsPackageVersion
+ type: string
+
+ # The type of build to test (Release or Debug)
+ - name: buildConfiguration
+ type: string
+ values:
+ - Release
+ - Debug
+
+ # The prefix to prepend to the job's display name:
+ #
+ # [] Run Stress Tests
+ #
+ - name: displayNamePrefix
+ type: string
+
+ # The suffix to append to the job name.
+ - name: jobNameSuffix
+ type: string
+
+ # The list of .NET Framework runtimes to test against.
+ - name: netFrameworkRuntimes
+ type: object
+ default: []
+
+ # The list of .NET runtimes to test against.
+ - name: netRuntimes
+ type: object
+ default: []
+
+ # The name of the Azure Pipelines pool to use.
+ - name: poolName
+ type: string
+
+ # The reference type to use:
+ #
+ # Project - dependent projects are referenced directly.
+ # Package - dependent projects are referenced via NuGet packages.
+ #
+ - name: referenceType
+ type: string
+ values:
+ - 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
+
+jobs:
+
+ - job: test_azure_package_job_${{ parameters.jobNameSuffix }}
+ displayName: '[${{ parameters.displayNamePrefix }}] Test Azure Package'
+ pool:
+ name: ${{ parameters.poolName }}
+
+ # Images provided by Azure Pipelines must be selected using 'vmImage'.
+ ${{ if eq(parameters.poolName, 'Azure Pipelines') }}:
+ vmImage: ${{ parameters.vmImage }}
+ # Images provided by 1ES must be selected using a demand.
+ ${{ else }}:
+ demands:
+ - imageOverride -equals ${{ parameters.vmImage }}
+
+ variables:
+
+ # The Azure test project file to use for all dotnet CLI commands.
+ - name: project
+ value: src/Microsoft.Data.SqlClient.Extensions/Azure/test/Azure.Test.csproj
+
+ # dotnet CLI arguments common to all commands.
+ - name: commonArguments
+ value: >-
+ --verbosity ${{ parameters.verbosity }}
+ -p:ReferenceType=${{ parameters.referenceType }}
+ -p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }}
+
+ # dotnet CLI arguments for build/test/pack commands
+ - name: buildArguments
+ value: >-
+ $(commonArguments)
+ --configuration ${{ parameters.buildConfiguration }}
+
+ # Explicitly unset the $PLATFORM environment variable that is set by the
+ # 'ADO Build properties' Library in the ADO SqlClientDrivers public
+ # project. This is defined with a non-standard Platform of 'AnyCPU', and
+ # will fail the builds if left defined. The stress tests solution does
+ # not require any specific Platform, and so its solution file doesn't
+ # support any non-standard platforms.
+ #
+ # Note that Azure Pipelines will inject this variable as PLATFORM into the
+ # environment of all tasks in this job.
+ #
+ # See:
+ # https://learn.microsoft.com/en-us/azure/devops/pipelines/process/variables?view=azure-devops&tabs=yaml%2Cbatch
+ #
+ - name: Platform
+ value: ''
+
+ # Do the same for $CONFIGURATION since we explicitly set it using our
+ # 'buildConfiguration' parameter, and we don't want the environment to
+ # override us.
+ - name: Configuration
+ value: ''
+
+ steps:
+
+ # We have a few extra steps for Package reference builds.
+ - ${{ if eq(parameters.referenceType, 'Package') }}:
+
+ # Download the Abstractions package artifacts into packages/.
+ - task: DownloadPipelineArtifact@2
+ displayName: Download Abstractions Package Artifact
+ inputs:
+ artifactName: ${{ parameters.abstractionsArtifactName }}
+ 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
+
+ # Install the .NET 9.0 SDK.
+ - task: UseDotNet@2
+ displayName: Install .NET 9.0 SDK
+ inputs:
+ packageType: sdk
+ version: 9.x
+
+ # Install the .NET 8.0 runtime.
+ - task: UseDotNet@2
+ displayName: Install .NET 8.0 Runtime
+ inputs:
+ packageType: runtime
+ version: 8.x
+
+ # The Windows agent images include a suitable .NET Framework runtime, so
+ # we don't have to install one explicitly.
+
+ # 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.
+ - task: DotNetCoreCLI@2
+ displayName: Restore Solution
+ inputs:
+ command: custom
+ custom: restore
+ projects: $(project)
+ arguments: $(commonArguments)
+
+ # Build the solution.
+ - task: DotNetCoreCLI@2
+ displayName: Build Solution
+ inputs:
+ command: custom
+ custom: build
+ projects: $(project)
+ arguments: $(buildArguments) --no-restore
+
+ # Run the tests for each .NET runtime.
+ - ${{ each runtime in parameters.netRuntimes }}:
+ - task: DotNetCoreCLI@2
+ displayName: Test [${{ runtime }}]
+ inputs:
+ command: custom
+ custom: test
+ projects: $(project)
+ arguments: $(buildArguments) --no-build -f ${{ runtime }}
+
+ # Run the tests for each .NET Framework runtime.
+ - ${{ each runtime in parameters.netFrameworkRuntimes }}:
+ - task: DotNetCoreCLI@2
+ displayName: Test [${{ runtime }}]
+ inputs:
+ command: custom
+ custom: test
+ projects: $(project)
+ arguments: $(buildArguments) --no-build -f ${{ runtime }}
diff --git a/eng/pipelines/libraries/ci-build-variables.yml b/eng/pipelines/libraries/ci-build-variables.yml
index 1a83c24cd8..f561c922b9 100644
--- a/eng/pipelines/libraries/ci-build-variables.yml
+++ b/eng/pipelines/libraries/ci-build-variables.yml
@@ -16,6 +16,8 @@ variables:
value: 1.0.0.$(buildNumber)-ci
- name: akvPackageVersion
value: 7.0.0.$(buildNumber)-ci
+ - name: azurePackageVersion
+ value: 1.0.0.$(buildNumber)-ci
- name: mdsPackageVersion
value: 7.0.0.$(buildNumber)-ci
- name: skipComponentGovernanceDetection
diff --git a/eng/pipelines/stages/build-abstractions-package-ci-stage.yml b/eng/pipelines/stages/build-abstractions-package-ci-stage.yml
index 2a9268e737..0d84dc6ccb 100644
--- a/eng/pipelines/stages/build-abstractions-package-ci-stage.yml
+++ b/eng/pipelines/stages/build-abstractions-package-ci-stage.yml
@@ -85,7 +85,7 @@ stages:
vmImage: windows-latest
buildConfiguration: ${{ parameters.buildConfiguration }}
netRuntimes: [net8.0, net9.0]
- netFrameworkRuntimes: [net462, net47, net471, net472, net48, net481]
+ netFrameworkRuntimes: [net462]
verbosity: ${{ parameters.verbosity }}
# ------------------------------------------------------------------------
diff --git a/eng/pipelines/stages/build-azure-package-ci-stage.yml b/eng/pipelines/stages/build-azure-package-ci-stage.yml
new file mode 100644
index 0000000000..90beb30caf
--- /dev/null
+++ b/eng/pipelines/stages/build-azure-package-ci-stage.yml
@@ -0,0 +1,162 @@
+################################################################################
+# 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.
+################################################################################
+
+# This stage builds the Azure package, runs tests, and publishes the resulting
+# NuGet packages as pipeline artifacts.
+#
+# The NuGet packages have the following properties:
+#
+# Name: Microsoft.Data.SqlClient.Extensions.Azure
+# Version: azurePackageVersion (from parameters)
+#
+# The following NuGet packages are published:
+#
+# Microsoft.Data.SqlClient.Extensions.Azure..nupkg
+# Microsoft.Data.SqlClient.Extensions.Azure..snupkg (symbols)
+#
+# The packages are published to pipeline artifacts with the name specified by
+# the azureArtifactName parameter.
+#
+# This template defines a stage named 'build_azure_package_stage' that
+# can be depended on by downstream stages.
+
+parameters:
+
+ # The name of the Abstractions pipeline artifact to download.
+ #
+ # This is used when the referenceType is 'Package'.
+ - name: abstractionsArtifactName
+ type: string
+ default: Abstractions.Artifact
+
+ # The Abstractions package verion to depend on.
+ #
+ # This is used when the referenceType is 'Package'.
+ - name: abstractionsPackageVersion
+ type: string
+
+ # The name of the pipeline artifact to publish.
+ - name: azureArtifactName
+ type: string
+ default: Azure.Artifact
+
+ # The version to apply to the NuGet package and DLLs.
+ - name: azurePackageVersion
+ type: string
+
+ # The type of build to produce (Release or Debug)
+ - name: buildConfiguration
+ type: string
+ default: Release
+ values:
+ - Release
+ - Debug
+
+ # The stages we depend on, if any.
+ - name: dependsOn
+ type: object
+ default: []
+
+ # The reference type to use:
+ #
+ # Project - dependent projects are referenced directly.
+ # Package - dependent projects are referenced via NuGet packages.
+ #
+ - name: referenceType
+ type: string
+ values:
+ - 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
+ displayName: Build Azure Package
+
+ dependsOn: ${{ parameters.dependsOn }}
+
+ jobs:
+
+ # ------------------------------------------------------------------------
+ # Build and test on Linux.
+
+ - template: ../jobs/test-azure-package-ci-job.yml@self
+ parameters:
+ abstractionsArtifactName: ${{ parameters.abstractionsArtifactName }}
+ abstractionsPackageVersion: ${{ parameters.abstractionsPackageVersion }}
+ buildConfiguration: ${{ parameters.buildConfiguration }}
+ displayNamePrefix: Linux
+ jobNameSuffix: linux
+ netFrameworkRuntimes: []
+ netRuntimes: [net8.0, net9.0]
+ poolName: Azure Pipelines
+ referenceType: ${{ parameters.referenceType }}
+ verbosity: ${{ parameters.verbosity }}
+ vmImage: ubuntu-latest
+
+ # ------------------------------------------------------------------------
+ # Build and test on Windows
+
+ - template: ../jobs/test-azure-package-ci-job.yml@self
+ parameters:
+ abstractionsArtifactName: ${{ parameters.abstractionsArtifactName }}
+ abstractionsPackageVersion: ${{ parameters.abstractionsPackageVersion }}
+ buildConfiguration: ${{ parameters.buildConfiguration }}
+ displayNamePrefix: Win
+ jobNameSuffix: windows
+ netFrameworkRuntimes: [net462]
+ netRuntimes: [net8.0, net9.0]
+ poolName: Azure Pipelines
+ referenceType: ${{ parameters.referenceType }}
+ verbosity: ${{ parameters.verbosity }}
+ vmImage: windows-latest
+
+ # ------------------------------------------------------------------------
+ # Build and test on macOS.
+
+ - template: ../jobs/test-azure-package-ci-job.yml
+ parameters:
+ abstractionsArtifactName: ${{ parameters.abstractionsArtifactName }}
+ abstractionsPackageVersion: ${{ parameters.abstractionsPackageVersion }}
+ buildConfiguration: ${{ parameters.buildConfiguration }}
+ displayNamePrefix: macOS
+ jobNameSuffix: macos
+ netFrameworkRuntimes: []
+ netRuntimes: [net8.0, net9.0]
+ poolName: Azure Pipelines
+ referenceType: ${{ parameters.referenceType }}
+ verbosity: ${{ parameters.verbosity }}
+ vmImage: macos-latest
+
+ # ------------------------------------------------------------------------
+ # Create and publish the NuGet package.
+
+ - template: ../jobs/pack-azure-package-ci-job.yml@self
+ parameters:
+ abstractionsArtifactName: ${{ parameters.abstractionsArtifactName }}
+ abstractionsPackageVersion: ${{ parameters.abstractionsPackageVersion }}
+ azureArtifactName: ${{ parameters.azureArtifactName }}
+ azurePackageVersion: ${{ parameters.abstractionsPackageVersion }}
+ buildConfiguration: ${{ parameters.buildConfiguration }}
+ 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_macos
+ referenceType: ${{ parameters.referenceType }}
+ verbosity: ${{ parameters.verbosity }}
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 1a41fd0a43..381a2d9817 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -12,12 +12,10 @@
Project
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/doc/Sample.xml b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/doc/Sample.xml
deleted file mode 100644
index 8d5f5c44d5..0000000000
--- a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/doc/Sample.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
- Sample class to demonstrate packaging and pipelines.
-
-
-
- Construct with a name.
- The name.
-
-
- Gets the name.
- The name.
-
-
-
diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/doc/SqlAuthenticationMethod.xml
similarity index 95%
rename from doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml
rename to src/Microsoft.Data.SqlClient.Extensions/Abstractions/doc/SqlAuthenticationMethod.xml
index cd15a65ec2..c804a8fb3a 100644
--- a/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationMethod.xml
+++ b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/doc/SqlAuthenticationMethod.xml
@@ -1,3 +1,8 @@
+
diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationParameters.xml b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/doc/SqlAuthenticationParameters.xml
similarity index 62%
rename from doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationParameters.xml
rename to src/Microsoft.Data.SqlClient.Extensions/Abstractions/doc/SqlAuthenticationParameters.xml
index 7c409cdc5c..087045b2c9 100644
--- a/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationParameters.xml
+++ b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/doc/SqlAuthenticationParameters.xml
@@ -1,4 +1,9 @@
-
+
+
@@ -6,23 +11,34 @@
- One of the enumeration values that specifies the authentication method.
+
+ Construct with values for all properties.
+
+ The authentication method.
The server name.
The database name.
The resource URI.
The authority URI.
- The user login name/ID.
- The user password.
+ The user login name/ID, or null if not applicable.
+ The user password, or null if not applicable.
The connection ID.
- The connection timeout value in seconds.
-
- Initializes a new instance of the class using the specified authentication method, server name, database name, resource URI, authority URI, user login name/ID, user password, connection ID and connection timeout value.
-
+
+ The authentication timeout, in seconds. The overall connection timeout
+ is managed by the driver; this timeout only applies to authentication.
+
Gets the authentication method.
The authentication method.
+
+ Gets the server name.
+ The server name.
+
+
+ Gets the database name.
+ The database name.
+
The resource URIs.
The resource URI.
@@ -33,27 +49,18 @@
Gets the user login name/ID.
- The user login name/ID.
+ The user login name/ID, or null if not applicable.
Gets the user password.
- The user password.
+ The user password, or null if not applicable.
Gets the connection ID.
The connection ID.
-
- Gets the server name.
- The server name.
-
-
- Gets the database name.
- The database name.
-
- Gets the connection timeout value.
- The connection timeout value to be passed to Cancellation Token Source.
+ Gets the authentication timeout value, in seconds.
diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationProvider.xml b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/doc/SqlAuthenticationProvider.xml
similarity index 92%
rename from doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationProvider.xml
rename to src/Microsoft.Data.SqlClient.Extensions/Abstractions/doc/SqlAuthenticationProvider.xml
index 75cc752d76..7848aaec1a 100644
--- a/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationProvider.xml
+++ b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/doc/SqlAuthenticationProvider.xml
@@ -1,4 +1,9 @@
-
+
+
Defines the core behavior of authentication providers and provides a base class for derived classes.
@@ -89,49 +94,50 @@
-
-
- Called from constructors in derived classes to initialize the class.
-
-
-
- The authentication method.
- Gets an authentication provider by method.
- The authentication provider or if not found.
-
-
- The authentication method.
- The authentication provider.
- Sets an authentication provider by method.
-
- if the operation succeeded; otherwise, (for example, the existing provider disallows overriding).
-
-
- The authentication method.
This method is called immediately before the provider is added to the SQL authentication provider registry.
Avoid performing long-waiting tasks in this method, since it can block other threads from accessing the provider registry.
+
+ This method must not throw.
+ The authentication method.
- The authentication method.
This method is called immediately before the provider is removed from the SQL authentication provider registry.
For example, this method is called when a different provider with the same authentication method overrides this provider in the SQL authentication provider registry. Avoid performing long-waiting task in this method, since it can block other threads from accessing the provider registry.
+
+ This method must not throw.
+ The authentication method.
- The authentication method.
Indicates whether the specified authentication method is supported.
+ This method must not throw.
if the specified authentication method is supported; otherwise, .
+ The authentication method.
- The Active Directory authentication parameters passed by the driver to authentication providers.
- Acquires a security token from the authority.
+ Acquires an access token from the authority.
+ The parameters passed to the provider by the driver.
+ If any errors occur.
Represents an asynchronous operation that returns the AD authentication token.
+
+ Gets an authentication provider by method.
+ The authentication method.
+ The authentication provider or if not found.
+
+
+ Sets an authentication provider by method.
+ The authentication method.
+ The authentication provider.
+
+ if the operation succeeded; otherwise, (for example, the existing provider disallows overriding).
+
+
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/doc/SqlAuthenticationProviderException.xml b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/doc/SqlAuthenticationProviderException.xml
new file mode 100644
index 0000000000..648e4a7e2f
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/doc/SqlAuthenticationProviderException.xml
@@ -0,0 +1,82 @@
+
+
+
+
+
+ This exception is thrown for any errors that occur during the
+ authentication process.
+
+
+
+
+ Protected construction for derived classes to supply a minimal set of
+ values.
+
+ Method will be NotSpecified.
+ FailureCode will be "Unknown".
+ ShouldRetry will be false.
+ RetryPeriod will be 0.
+
+ The error message.
+ The exception that caused this exception, if any.
+
+
+
+ Protected construction for derived classes to supply values for all
+ public properties.
+
+
+ The authentication method that failed, or NotSpecified if not known.
+
+
+ The failure code, or "Unknown" if not known.
+
+
+ True if the action should be retried, false otherwise.
+
+
+ The period of time, in milliseconds, to wait before retrying the action.
+ Specify 0 if no retry period is suggested. Ignored if negative. Not
+ used when ShouldRetry is false, in which cases 0 is assumed.
+
+
+ The error message.
+
+
+ The exception that caused this exception, if any.
+
+
+
+
+ The authentication method that failed, or NotSpecified if not known.
+
+
+
+
+ The failure code, or "Unknown" if not known.
+
+
+
+
+ True if the action should be retried, false otherwise.
+
+
+
+
+ The period of time, in milliseconds, to wait before retrying the action.
+ 0 if no retry period is suggested. Never negative. Always 0 when
+ ShouldRetry is false.
+
+
+
+
+ A string that includes the base exception information along with all
+ property values.
+
+
+
+
diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationToken.xml b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/doc/SqlAuthenticationToken.xml
similarity index 55%
rename from doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationToken.xml
rename to src/Microsoft.Data.SqlClient.Extensions/Abstractions/doc/SqlAuthenticationToken.xml
index 52f79dd535..4eb9a78f52 100644
--- a/doc/snippets/Microsoft.Data.SqlClient/SqlAuthenticationToken.xml
+++ b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/doc/SqlAuthenticationToken.xml
@@ -1,25 +1,30 @@
-
+
+
Represents an authentication token.
- The access token.
- The token expiration time.
- Initializes a new instance of the class.
+ Construct with values for all properties.
-
- The parameter is or empty.
-
+ The token string.
+ The token expiration time.
+
+ is null or empty.
+
-
- Gets the token expiration time.
- The token expiration time.
-
Gets the token string.
The token string.
+
+ Gets the token expiration time.
+ The token expiration time.
+
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Abstractions.csproj b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Abstractions.csproj
index 485a3de83a..9436e9067b 100644
--- a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Abstractions.csproj
+++ b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Abstractions.csproj
@@ -23,26 +23,41 @@
Microsoft.Data.SqlClient.Extensions.Abstractions
- Microsoft.Data.SqlClient.Extensions.Abstractions
+
+
+
- $(_DefaultMajorVersion).0.0.0
+ $(AbstractionsDefaultMajorVersion).0.0.0
$(AbstractionsAssemblyFileVersion)
$(AbstractionsAssemblyFileVersion)
$(AbstractionsPackageVersion)
+
+ $(Artifacts)/doc/$(TargetFramework)/$(AssemblyName).xml
<_Parameter1>true
+
+
-
+ CDP_BUILD_TYPE is a OneBranch governed pipeline variable.
+ -->
+
+
- <_DefaultMajorVersion>1
+ 1
<_OurPackageVersion Condition="'$(AbstractionsPackageVersion)' != ''">$(AbstractionsPackageVersion)
- <_OurPackageVersion Condition="'$(AbstractionsPackageVersion)' == ''">$(_DefaultMajorVersion).0.0.$(BuildNumber)-dev
+ <_OurPackageVersion Condition="'$(AbstractionsPackageVersion)' == ''">$(AbstractionsDefaultMajorVersion).0.0.$(BuildNumber)-dev
@@ -56,7 +56,7 @@
<_OurAssemblyFileVersion Condition="'$(AbstractionsAssemblyFileVersion)' == '' and '$(AbstractionsPackageVersion)' != ''">$(AbstractionsPackageVersion.Split('-')[0])
- <_OurAssemblyFileVersion Condition="'$(AbstractionsAssemblyFileVersion)' == '' and '$(AbstractionsPackageVersion)' == ''">$(_DefaultMajorVersion).0.0.$(BuildNumber)
+ <_OurAssemblyFileVersion Condition="'$(AbstractionsAssemblyFileVersion)' == '' and '$(AbstractionsPackageVersion)' == ''">$(AbstractionsDefaultMajorVersion).0.0.$(BuildNumber)
+
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Azure/src/ActiveDirectoryAuthenticationProvider.cs b/src/Microsoft.Data.SqlClient.Extensions/Azure/src/ActiveDirectoryAuthenticationProvider.cs
new file mode 100644
index 0000000000..ac40d8fdea
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient.Extensions/Azure/src/ActiveDirectoryAuthenticationProvider.cs
@@ -0,0 +1,896 @@
+// 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.Collections.Concurrent;
+using System.Diagnostics;
+using System.Security.Cryptography;
+using System.Text;
+using Azure.Core;
+using Azure.Identity;
+using Microsoft.Extensions.Caching.Memory;
+using Microsoft.Identity.Client;
+using Microsoft.Identity.Client.Extensibility;
+
+namespace Microsoft.Data.SqlClient;
+
+///
+public sealed class ActiveDirectoryAuthenticationProvider : SqlAuthenticationProvider
+{
+ ///
+ /// This is a static cache instance meant to hold instances of "PublicClientApplication" mapping to information available in PublicClientAppKey.
+ /// The purpose of this cache is to allow re-use of Access Tokens fetched for a user interactively or with any other mode
+ /// to avoid interactive authentication request every-time, within application scope making use of MSAL's userTokenCache.
+ ///
+ private static readonly ConcurrentDictionary s_pcaMap = new();
+ private static readonly ConcurrentDictionary s_tokenCredentialMap = new();
+ private static SemaphoreSlim s_pcaMapModifierSemaphore = new(1, 1);
+ private static SemaphoreSlim s_tokenCredentialMapModifierSemaphore = new(1, 1);
+ private static readonly MemoryCache s_accountPwCache = new MemoryCache(new MemoryCacheOptions());
+ private const int s_accountPwCacheTtlInHours = 2;
+ private const string s_nativeClientRedirectUri = "https://login.microsoftonline.com/common/oauth2/nativeclient";
+ private const string s_defaultScopeSuffix = "/.default";
+ private readonly string _type = typeof(ActiveDirectoryAuthenticationProvider).Name;
+ private readonly SqlClientLogger _logger = new();
+ private Func _deviceCodeFlowCallback;
+ private ICustomWebUi? _customWebUI = null;
+ private readonly string _applicationClientId = "2fd908ad-0664-4344-b9be-cd3e8b574c38";
+
+ // The MSAL error code that indicates the action should be retried.
+ //
+ // https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/retry-after#simple-retry-for-errors-with-http-error-codes-500-600
+ private const int MsalRetryStatusCode = 429;
+
+ ///
+ public ActiveDirectoryAuthenticationProvider()
+ : this(DefaultDeviceFlowCallback)
+ {
+ }
+
+ ///
+ public ActiveDirectoryAuthenticationProvider(string applicationClientId)
+ : this(DefaultDeviceFlowCallback, applicationClientId)
+ {
+ }
+
+ ///
+ public ActiveDirectoryAuthenticationProvider(Func deviceCodeFlowCallbackMethod, string? applicationClientId = null)
+ {
+ _deviceCodeFlowCallback = deviceCodeFlowCallbackMethod;
+ if (applicationClientId is not null)
+ {
+ _applicationClientId = applicationClientId;
+ }
+ }
+
+ ///
+ public static void ClearUserTokenCache()
+ {
+ if (!s_pcaMap.IsEmpty)
+ {
+ s_pcaMap.Clear();
+ }
+
+ if (!s_tokenCredentialMap.IsEmpty)
+ {
+ s_tokenCredentialMap.Clear();
+ }
+ }
+
+ ///
+ public void SetDeviceCodeFlowCallback(Func deviceCodeFlowCallbackMethod) => _deviceCodeFlowCallback = deviceCodeFlowCallbackMethod;
+
+ ///
+ public void SetAcquireAuthorizationCodeAsyncCallback(Func> acquireAuthorizationCodeAsyncCallback) => _customWebUI = new CustomWebUi(acquireAuthorizationCodeAsyncCallback);
+
+ ///
+ public override bool IsSupported(SqlAuthenticationMethod authentication)
+ {
+ return authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated
+ #pragma warning disable CS0618 // Type or member is obsolete
+ || authentication == SqlAuthenticationMethod.ActiveDirectoryPassword
+ #pragma warning restore CS0618 // Type or member is obsolete
+ || authentication == SqlAuthenticationMethod.ActiveDirectoryInteractive
+ || authentication == SqlAuthenticationMethod.ActiveDirectoryServicePrincipal
+ || authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow
+ || authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity
+ || authentication == SqlAuthenticationMethod.ActiveDirectoryMSI
+ || authentication == SqlAuthenticationMethod.ActiveDirectoryDefault
+ || authentication == SqlAuthenticationMethod.ActiveDirectoryWorkloadIdentity;
+ }
+
+ ///
+ public override void BeforeLoad(SqlAuthenticationMethod authentication)
+ {
+ _logger.LogInfo(_type, "BeforeLoad", $"being loaded into SqlAuthProviders for {authentication}.");
+ }
+
+ ///
+ public override void BeforeUnload(SqlAuthenticationMethod authentication)
+ {
+ _logger.LogInfo(_type, "BeforeUnload", $"being unloaded from SqlAuthProviders for {authentication}.");
+ }
+
+ #if NETFRAMEWORK
+ private Func _iWin32WindowFunc = null;
+
+ ///
+ public void SetIWin32WindowFunc(Func iWin32WindowFunc) => this._iWin32WindowFunc = iWin32WindowFunc;
+ #endif
+
+ ///
+ public override async Task AcquireTokenAsync(SqlAuthenticationParameters parameters)
+ {
+ try
+ {
+ using CancellationTokenSource cts = new();
+
+ // Use the authentication timeout value to cancel token acquire
+ // request after certain period of time.
+ if (parameters.ConnectionTimeout > 0)
+ {
+ // Safely convert to milliseconds.
+ if (int.MaxValue / 1000 > parameters.ConnectionTimeout)
+ {
+ cts.CancelAfter(int.MaxValue);
+ }
+ else
+ {
+ cts.CancelAfter(parameters.ConnectionTimeout * 1000);
+ }
+ }
+
+ string scope = parameters.Resource.EndsWith(s_defaultScopeSuffix, StringComparison.Ordinal) ? parameters.Resource : parameters.Resource + s_defaultScopeSuffix;
+ string[] scopes = [scope];
+ TokenRequestContext tokenRequestContext = new(scopes);
+
+ // We split audience from Authority URL here. Audience can be one of
+ // the following:
+ //
+ // - The Azure AD authority audience enumeration
+ // - The tenant ID, which can be:
+ // - A GUID (the ID of your Azure AD instance), for
+ // single-tenant applications
+ // - A domain name associated with your Azure AD instance (also
+ // for single-tenant applications)
+ // - One of these placeholders as a tenant ID in place of the
+ // Azure AD authority audience enumeration:
+ // - `organizations` for a multitenant application
+ // - `consumers` to sign in users only with their personal
+ // accounts
+ // - `common` to sign in users with their work and school
+ // accounts or their personal Microsoft accounts
+ //
+ // MSAL will throw a meaningful exception if you specify both the
+ // Azure AD authority audience and the tenant ID.
+ //
+ // If you don't specify an audience, your app will target Azure AD
+ // and personal Microsoft accounts as an audience. (That is, it
+ // will behave as though `common` were specified.)
+ //
+ // More information:
+ //
+ // https://docs.microsoft.com/azure/active-directory/develop/msal-client-application-configuration
+
+ int separatorIndex = parameters.Authority.LastIndexOf('/');
+ string authority = parameters.Authority.Remove(separatorIndex + 1);
+ string audience = parameters.Authority.Substring(separatorIndex + 1);
+ string? clientId = string.IsNullOrWhiteSpace(parameters.UserId) ? null : parameters.UserId;
+
+ if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryDefault)
+ {
+ // Cache DefaultAzureCredenial based on scope, authority, audience, and clientId
+ TokenCredentialKey tokenCredentialKey = new(typeof(DefaultAzureCredential), authority, scope, audience, clientId);
+ AccessToken accessToken = await GetTokenAsync(tokenCredentialKey, string.Empty, tokenRequestContext, cts.Token).ConfigureAwait(false);
+ SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Default auth mode. Expiry Time: {0}", accessToken.ExpiresOn);
+ return new SqlAuthenticationToken(accessToken.Token, accessToken.ExpiresOn);
+ }
+
+ TokenCredentialOptions tokenCredentialOptions = new() { AuthorityHost = new Uri(authority) };
+
+ if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryMSI)
+ {
+ // Cache ManagedIdentityCredential based on scope, authority, and clientId
+ TokenCredentialKey tokenCredentialKey = new(typeof(ManagedIdentityCredential), authority, scope, string.Empty, clientId);
+ AccessToken accessToken = await GetTokenAsync(tokenCredentialKey, string.Empty, tokenRequestContext, cts.Token).ConfigureAwait(false);
+ SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Managed Identity auth mode. Expiry Time: {0}", accessToken.ExpiresOn);
+ return new SqlAuthenticationToken(accessToken.Token, accessToken.ExpiresOn);
+ }
+
+ if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryServicePrincipal)
+ {
+ // Cache ClientSecretCredential based on scope, authority, audience, and clientId
+ TokenCredentialKey tokenCredentialKey = new(typeof(ClientSecretCredential), authority, scope, audience, clientId);
+ string password = parameters.Password is null ? string.Empty : parameters.Password;
+ AccessToken accessToken = await GetTokenAsync(tokenCredentialKey, password, tokenRequestContext, cts.Token).ConfigureAwait(false);
+ SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Active Directory Service Principal auth mode. Expiry Time: {0}", accessToken.ExpiresOn);
+ return new SqlAuthenticationToken(accessToken.Token, accessToken.ExpiresOn);
+ }
+
+ if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryWorkloadIdentity)
+ {
+ // Cache WorkloadIdentityCredential based on authority and clientId
+ TokenCredentialKey tokenCredentialKey = new(typeof(WorkloadIdentityCredential), authority, string.Empty, string.Empty, clientId);
+ // If either tenant id, client id, or the token file path are not specified when fetching the token,
+ // a CredentialUnavailableException will be thrown instead
+ AccessToken accessToken = await GetTokenAsync(tokenCredentialKey, string.Empty, tokenRequestContext, cts.Token).ConfigureAwait(false);
+ SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Workload Identity auth mode. Expiry Time: {0}", accessToken.ExpiresOn);
+ return new SqlAuthenticationToken(accessToken.Token, accessToken.ExpiresOn);
+ }
+
+ /*
+ * Today, MSAL.NET uses another redirect URI by default in desktop applications that run on Windows
+ * (urn:ietf:wg:oauth:2.0:oob). In the future, we'll want to change this default, so we recommend
+ * that you use https://login.microsoftonline.com/common/oauth2/nativeclient.
+ *
+ * https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-desktop-app-registration#redirect-uris
+ */
+ string redirectUri = s_nativeClientRedirectUri;
+
+ #if NET
+ if (parameters.AuthenticationMethod != SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow)
+ {
+ redirectUri = "http://localhost";
+ }
+ #endif
+
+ PublicClientAppKey pcaKey =
+ #if NETFRAMEWORK
+ new(parameters.Authority, redirectUri, _applicationClientId, _iWin32WindowFunc);
+ #else
+ new(parameters.Authority, redirectUri, _applicationClientId);
+ #endif
+
+ AuthenticationResult? result = null;
+ IPublicClientApplication app = await GetPublicClientAppInstanceAsync(pcaKey, cts.Token).ConfigureAwait(false);
+
+ if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryIntegrated)
+ {
+ result = await TryAcquireTokenSilent(app, parameters, scopes, cts).ConfigureAwait(false);
+
+ if (result == null)
+ {
+ // The AcquireTokenByIntegratedWindowsAuth method is marked
+ // as obsolete in MSAL.NET but it is still a supported way
+ // to acquire tokens for Active Directory Integrated
+ // authentication.
+ var builder =
+ #pragma warning disable CS0618 // Type or member is obsolete
+ app.AcquireTokenByIntegratedWindowsAuth(scopes)
+ #pragma warning restore CS0618 // Type or member is obsolete
+ .WithCorrelationId(parameters.ConnectionId);
+
+ if (!string.IsNullOrEmpty(parameters.UserId))
+ {
+ builder = builder.WithUsername(parameters.UserId);
+ }
+
+ result = await builder
+ .ExecuteAsync(cancellationToken: cts.Token)
+ .ConfigureAwait(false);
+
+ SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Active Directory Integrated auth mode. Expiry Time: {0}", result?.ExpiresOn);
+ }
+ }
+ #pragma warning disable CS0618 // Type or member is obsolete
+ else if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryPassword)
+ #pragma warning restore CS0618 // Type or member is obsolete
+ {
+ string pwCacheKey = GetAccountPwCacheKey(parameters);
+ object? previousPw = s_accountPwCache.Get(pwCacheKey);
+ string password = parameters.Password is null ? string.Empty : parameters.Password;
+ byte[] currPwHash = GetHash(password);
+
+ if (previousPw != null &&
+ previousPw is byte[] previousPwBytes &&
+ // Only get the cached token if the current password hash matches the previously used password hash
+ AreEqual(currPwHash, previousPwBytes))
+ {
+ result = await TryAcquireTokenSilent(app, parameters, scopes, cts).ConfigureAwait(false);
+ }
+
+ if (result == null)
+ {
+ #pragma warning disable CS0618 // Type or member is obsolete
+ result = await app.AcquireTokenByUsernamePassword(scopes, parameters.UserId, parameters.Password)
+ #pragma warning disable CS0618 // Type or member is obsolete
+ .WithCorrelationId(parameters.ConnectionId)
+ .ExecuteAsync(cancellationToken: cts.Token)
+ .ConfigureAwait(false);
+
+ // We cache the password hash to ensure future connection requests include a validated password
+ // when we check for a cached MSAL account. Otherwise, a connection request with the same username
+ // against the same tenant could succeed with an invalid password when we re-use the cached token.
+ using (ICacheEntry entry = s_accountPwCache.CreateEntry(pwCacheKey))
+ {
+ entry.Value = currPwHash;
+ entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(s_accountPwCacheTtlInHours);
+ }
+
+ SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Active Directory Password auth mode. Expiry Time: {0}", result?.ExpiresOn);
+ }
+ }
+ else if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryInteractive ||
+ parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow)
+ {
+ try
+ {
+ result = await TryAcquireTokenSilent(app, parameters, scopes, cts).ConfigureAwait(false);
+ SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (silent) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, result?.ExpiresOn);
+ }
+ catch (MsalUiRequiredException)
+ {
+ // An 'MsalUiRequiredException' is thrown in the case where an interaction is required with the end user of the application,
+ // for instance, if no refresh token was in the cache, or the user needs to consent, or re-sign-in (for instance if the password expired),
+ // or the user needs to perform two factor authentication.
+ //
+ // result should be null here, but we make sure of that.
+ Debug.Assert(result is null);
+ result = null;
+ }
+
+ if (result == null)
+ {
+ // If no existing 'account' is found, we request user to sign in interactively.
+ result = await AcquireTokenInteractiveDeviceFlowAsync(app, scopes, parameters.ConnectionId, parameters.UserId, parameters.AuthenticationMethod, cts, _customWebUI, _deviceCodeFlowCallback).ConfigureAwait(false);
+ SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (interactive) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, result?.ExpiresOn);
+ }
+ }
+ else
+ {
+ SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | {0} authentication mode not supported by ActiveDirectoryAuthenticationProvider class.", parameters.AuthenticationMethod);
+
+ throw new Extensions.Azure.AuthenticationException(
+ parameters.AuthenticationMethod,
+ $"Authentication method {parameters.AuthenticationMethod} not supported.");
+ }
+
+ // TODO: Existing bug? result may be null here.
+ if (result is null)
+ {
+ throw new Extensions.Azure.AuthenticationException(
+ parameters.AuthenticationMethod,
+ "Internal error - authentication result is null");
+ }
+
+ return new SqlAuthenticationToken(result.AccessToken, result.ExpiresOn);
+ }
+ catch (MsalException ex)
+ {
+ // Check for an explicitly retryable error.
+ if (ex is MsalServiceException svcEx &&
+ svcEx.StatusCode == MsalRetryStatusCode)
+ {
+ int retryPeriod = 0;
+
+ var retryAfter = svcEx.Headers.RetryAfter;
+ if (retryAfter is not null)
+ {
+ if (retryAfter.Delta.HasValue)
+ {
+ retryPeriod = retryAfter.Delta.Value.Milliseconds;
+ }
+ else if (retryAfter.Date.HasValue)
+ {
+ retryPeriod = Convert.ToInt32(retryAfter.Date.Value.Offset.TotalMilliseconds);
+ }
+
+ throw new Extensions.Azure.AuthenticationException(
+ parameters.AuthenticationMethod,
+ ex.ErrorCode,
+ true,
+ retryPeriod,
+ ex.Message,
+ ex);
+ }
+
+ // Fall through to check the ErrorCode...
+ }
+
+ // Check for an unknown error, which we will treat as implicitly
+ // retryable, but without a suggested period.
+ if (ex.ErrorCode == MsalError.UnknownError)
+ {
+ throw new Extensions.Azure.AuthenticationException(
+ parameters.AuthenticationMethod,
+ ex.ErrorCode,
+ true,
+ // Don't suggest a retry period.
+ 0,
+ ex.Message,
+ ex);
+ }
+
+ // The error isn't retryable.
+ throw new Extensions.Azure.AuthenticationException(
+ parameters.AuthenticationMethod,
+ ex.ErrorCode,
+ false,
+ 0,
+ ex.Message,
+ ex);
+ }
+ catch (Exception ex)
+ when (ex is
+ AuthenticationFailedException or
+ AuthenticationRequiredException or
+ CredentialUnavailableException)
+ {
+ // These errors aren't retryable.
+ throw new Extensions.Azure.AuthenticationException(
+ parameters.AuthenticationMethod,
+ "Unknown",
+ false,
+ 0,
+ $"Azure.Identity error: {ex.Message}",
+ ex);
+ }
+ catch (Exception ex)
+ {
+ // These errors aren't retryable.
+ throw new Extensions.Azure.AuthenticationException(
+ parameters.AuthenticationMethod,
+ "Unknown",
+ false,
+ 0,
+ $"Unexpected error: {ex.Message}",
+ ex);
+ }
+ }
+
+ private static async Task TryAcquireTokenSilent(IPublicClientApplication app, SqlAuthenticationParameters parameters,
+ string[] scopes, CancellationTokenSource cts)
+ {
+ AuthenticationResult? result = null;
+
+ // Fetch available accounts from 'app' instance
+ System.Collections.Generic.IEnumerator accounts = (await app.GetAccountsAsync().ConfigureAwait(false)).GetEnumerator();
+
+ IAccount? account = default;
+ if (accounts.MoveNext())
+ {
+ if (!string.IsNullOrEmpty(parameters.UserId))
+ {
+ do
+ {
+ IAccount currentVal = accounts.Current;
+ if (string.Compare(parameters.UserId, currentVal.Username, StringComparison.InvariantCultureIgnoreCase) == 0)
+ {
+ account = currentVal;
+ break;
+ }
+ }
+ while (accounts.MoveNext());
+ }
+ else
+ {
+ account = accounts.Current;
+ }
+ }
+
+ if (account != null)
+ {
+ // If 'account' is available in 'app', we use the same to acquire token silently.
+ // Read More on API docs: https://docs.microsoft.com/dotnet/api/microsoft.identity.client.clientapplicationbase.acquiretokensilent
+ result = await app.AcquireTokenSilent(scopes, account).ExecuteAsync(cancellationToken: cts.Token).ConfigureAwait(false);
+ SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (silent) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, result?.ExpiresOn);
+ }
+
+ return result;
+ }
+
+ private static async Task AcquireTokenInteractiveDeviceFlowAsync(IPublicClientApplication app, string[] scopes, Guid connectionId, string? userId,
+ SqlAuthenticationMethod authenticationMethod, CancellationTokenSource cts, ICustomWebUi? customWebUI, Func deviceCodeFlowCallback)
+ {
+ try
+ {
+ if (authenticationMethod == SqlAuthenticationMethod.ActiveDirectoryInteractive)
+ {
+ CancellationTokenSource ctsInteractive = new();
+ #if NET
+ // On .NET Core, MSAL will start the system browser as a
+ // separate process. MSAL does not have control over this
+ // browser, but once the user finishes authentication, the web
+ // page is redirected in such a way that MSAL can intercept the
+ // Uri. MSAL cannot detect if the user navigates away or simply
+ // closes the browser. Apps using this technique are encouraged
+ // to define a timeout (via CancellationToken). We recommend a
+ // timeout of at least a few minutes, to take into account cases
+ // where the user is prompted to change password or perform 2FA.
+ //
+ // https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/System-Browser-on-.Net-Core#system-browser-experience
+ //
+ // Wait up to 3 minutes.
+ ctsInteractive.CancelAfter(180000);
+ #endif
+ if (customWebUI != null)
+ {
+ return await app.AcquireTokenInteractive(scopes)
+ .WithCorrelationId(connectionId)
+ .WithCustomWebUi(customWebUI)
+ .WithLoginHint(userId)
+ .ExecuteAsync(ctsInteractive.Token)
+ .ConfigureAwait(false);
+ }
+ else
+ {
+ /*
+ * We will use the MSAL Embedded or System web browser which changes by Default in MSAL according to this table:
+ *
+ * Framework Embedded System Default
+ * -------------------------------------------
+ * .NET Classic Yes Yes^ Embedded
+ * .NET Core No Yes^ System
+ * .NET Standard No No NONE
+ * UWP Yes No Embedded
+ * Xamarin.Android Yes Yes System
+ * Xamarin.iOS Yes Yes System
+ * Xamarin.Mac Yes No Embedded
+ *
+ * ^ Requires "http://localhost" redirect URI
+ *
+ * https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/MSAL.NET-uses-web-browser#at-a-glance
+ */
+ return await app.AcquireTokenInteractive(scopes)
+ .WithCorrelationId(connectionId)
+ .WithLoginHint(userId)
+ .ExecuteAsync(ctsInteractive.Token)
+ .ConfigureAwait(false);
+ }
+ }
+ else
+ {
+ return await app.AcquireTokenWithDeviceCode(scopes,
+ deviceCodeResult => deviceCodeFlowCallback(deviceCodeResult))
+ .WithCorrelationId(connectionId)
+ .ExecuteAsync(cancellationToken: cts.Token)
+ .ConfigureAwait(false);
+ }
+ }
+ catch (OperationCanceledException ex)
+ {
+ SqlClientEventSource.Log.TryTraceEvent("AcquireTokenInteractiveDeviceFlowAsync | Operation timed out while acquiring access token.");
+
+ throw new Extensions.Azure.AuthenticationException(
+ authenticationMethod,
+ "OperationCanceled",
+ false,
+ 0,
+ // TODO: This used to use the following localized strings
+ // depending on the method:
+ //
+ // Strings.SQL_Timeout_Active_Directory_Interactive_Authentication
+ // Strings.SQL_Timeout_Active_Directory_DeviceFlow_Authentication
+ ex.Message,
+ ex);
+ }
+ }
+
+ private static Task DefaultDeviceFlowCallback(DeviceCodeResult result)
+ {
+ // This will print the message on the console which tells the user where to go sign-in using
+ // a separate browser and the code to enter once they sign in.
+ // The AcquireTokenWithDeviceCode() method will poll the server after firing this
+ // device code callback to look for the successful login of the user via that browser.
+ // This background polling (whose interval and timeout data is also provided as fields in the
+ // deviceCodeCallback class) will occur until:
+ // * The user has successfully logged in via browser and entered the proper code
+ // * The timeout specified by the server for the lifetime of this code (typically ~15 minutes) has been reached
+ // * The developing application calls the Cancel() method on a CancellationToken sent into the method.
+ // If this occurs, an OperationCanceledException will be thrown (see catch below for more details).
+ SqlClientEventSource.Log.TryTraceEvent("AcquireTokenInteractiveDeviceFlowAsync | Callback triggered with Device Code Result: {0}", result.Message);
+ Console.WriteLine(result.Message);
+ return Task.FromResult(0);
+ }
+
+ private class CustomWebUi : ICustomWebUi
+ {
+ private readonly Func> _acquireAuthorizationCodeAsyncCallback;
+
+ internal CustomWebUi(Func> acquireAuthorizationCodeAsyncCallback) => _acquireAuthorizationCodeAsyncCallback = acquireAuthorizationCodeAsyncCallback;
+
+ public Task AcquireAuthorizationCodeAsync(Uri authorizationUri, Uri redirectUri, CancellationToken cancellationToken)
+ => _acquireAuthorizationCodeAsyncCallback.Invoke(authorizationUri, redirectUri, cancellationToken);
+ }
+
+ private async Task GetPublicClientAppInstanceAsync(PublicClientAppKey publicClientAppKey, CancellationToken cancellationToken)
+ {
+ if (!s_pcaMap.TryGetValue(publicClientAppKey, out IPublicClientApplication clientApplicationInstance))
+ {
+ await s_pcaMapModifierSemaphore.WaitAsync(cancellationToken);
+ try
+ {
+ // Double-check in case another thread added it while we waited for the semaphore
+ if (!s_pcaMap.TryGetValue(publicClientAppKey, out clientApplicationInstance))
+ {
+ clientApplicationInstance = CreateClientAppInstance(publicClientAppKey);
+ s_pcaMap.TryAdd(publicClientAppKey, clientApplicationInstance);
+ }
+ }
+ finally
+ {
+ s_pcaMapModifierSemaphore.Release();
+ }
+ }
+
+ return clientApplicationInstance;
+ }
+
+ private static async Task GetTokenAsync(TokenCredentialKey tokenCredentialKey, string secret,
+ TokenRequestContext tokenRequestContext, CancellationToken cancellationToken)
+ {
+ if (!s_tokenCredentialMap.TryGetValue(tokenCredentialKey, out TokenCredentialData tokenCredentialInstance))
+ {
+ await s_tokenCredentialMapModifierSemaphore.WaitAsync(cancellationToken);
+ try
+ {
+ // Double-check in case another thread added it while we waited for the semaphore
+ if (!s_tokenCredentialMap.TryGetValue(tokenCredentialKey, out tokenCredentialInstance))
+ {
+ tokenCredentialInstance = CreateTokenCredentialInstance(tokenCredentialKey, secret);
+ s_tokenCredentialMap.TryAdd(tokenCredentialKey, tokenCredentialInstance);
+ }
+ }
+ finally
+ {
+ s_tokenCredentialMapModifierSemaphore.Release();
+ }
+ }
+
+ if (!AreEqual(tokenCredentialInstance._secretHash, GetHash(secret)))
+ {
+ // If the secret hash has changed, we need to remove the old token credential instance and create a new one.
+ await s_tokenCredentialMapModifierSemaphore.WaitAsync(cancellationToken);
+ try
+ {
+ s_tokenCredentialMap.TryRemove(tokenCredentialKey, out _);
+ tokenCredentialInstance = CreateTokenCredentialInstance(tokenCredentialKey, secret);
+ s_tokenCredentialMap.TryAdd(tokenCredentialKey, tokenCredentialInstance);
+ }
+ finally
+ {
+ s_tokenCredentialMapModifierSemaphore.Release();
+ }
+ }
+
+ return await tokenCredentialInstance._tokenCredential.GetTokenAsync(tokenRequestContext, cancellationToken);
+ }
+
+ private static string GetAccountPwCacheKey(SqlAuthenticationParameters parameters)
+ {
+ return parameters.Authority + "+" + parameters.UserId;
+ }
+
+ private static byte[] GetHash(string input)
+ {
+ byte[] unhashedBytes = Encoding.Unicode.GetBytes(input);
+ SHA256 sha256 = SHA256.Create();
+ byte[] hashedBytes = sha256.ComputeHash(unhashedBytes);
+ return hashedBytes;
+ }
+
+ private static bool AreEqual(byte[] a1, byte[] a2)
+ {
+ if (ReferenceEquals(a1, a2))
+ {
+ return true;
+ }
+ else if (a1 is null || a2 is null)
+ {
+ return false;
+ }
+ else if (a1.Length != a2.Length)
+ {
+ return false;
+ }
+
+ return a1.AsSpan().SequenceEqual(a2.AsSpan());
+ }
+
+ private IPublicClientApplication CreateClientAppInstance(PublicClientAppKey publicClientAppKey)
+ {
+ PublicClientApplicationBuilder builder = PublicClientApplicationBuilder
+ .CreateWithApplicationOptions(new PublicClientApplicationOptions
+ {
+ ClientId = publicClientAppKey._applicationClientId,
+ ClientName = typeof(ActiveDirectoryAuthenticationProvider).FullName,
+ ClientVersion = Extensions.Azure.ThisAssembly.InformationalVersion,
+ RedirectUri = publicClientAppKey._redirectUri,
+ })
+ .WithAuthority(publicClientAppKey._authority);
+
+ #if NETFRAMEWORK
+ if (_iWin32WindowFunc is not null)
+ {
+ builder.WithParentActivityOrWindow(_iWin32WindowFunc);
+ }
+ #endif
+
+ return builder.Build();
+ }
+
+ private static TokenCredentialData CreateTokenCredentialInstance(TokenCredentialKey tokenCredentialKey, string secret)
+ {
+ if (tokenCredentialKey._tokenCredentialType == typeof(DefaultAzureCredential))
+ {
+ DefaultAzureCredentialOptions defaultAzureCredentialOptions = new()
+ {
+ AuthorityHost = new Uri(tokenCredentialKey._authority),
+ TenantId = tokenCredentialKey._audience,
+ ExcludeInteractiveBrowserCredential = true // Force disabled, even though it's disabled by default to respect driver specifications.
+ };
+
+ // Optionally set clientId when available
+ if (tokenCredentialKey._clientId is not null)
+ {
+ defaultAzureCredentialOptions.ManagedIdentityClientId = tokenCredentialKey._clientId;
+ defaultAzureCredentialOptions.SharedTokenCacheUsername = tokenCredentialKey._clientId;
+ defaultAzureCredentialOptions.WorkloadIdentityClientId = tokenCredentialKey._clientId;
+ }
+
+ // SqlClient is a library and provides support to acquire access
+ // token using 'DefaultAzureCredential' on user demand when they
+ // specify 'Authentication = Active Directory Default' in
+ // connection string.
+ //
+ // Default Azure Credential is instantiated by the calling
+ // application when using "Active Directory Default"
+ // authentication code to connect to Azure SQL instance.
+ // SqlClient is a library, doesn't instantiate the credential
+ // without running application instructions.
+ //
+ // Note that CodeQL suppression support can only detect
+ // suppression comments that appear immediately above the
+ // flagged statement, or appended to the end of the statement.
+ // Multi-line justifications are not supported.
+ //
+ // https://eng.ms/docs/cloud-ai-platform/devdiv/one-engineering-system-1es/1es-docs/codeql/codeql-semmle#guidance-on-suppressions
+ //
+ // CodeQL [SM05137] See above for justification.
+ DefaultAzureCredential cred = new(defaultAzureCredentialOptions);
+
+ return new TokenCredentialData(cred, GetHash(secret));
+ }
+
+ TokenCredentialOptions tokenCredentialOptions = new() { AuthorityHost = new Uri(tokenCredentialKey._authority) };
+
+ if (tokenCredentialKey._tokenCredentialType == typeof(ManagedIdentityCredential))
+ {
+ return new TokenCredentialData(new ManagedIdentityCredential(tokenCredentialKey._clientId, tokenCredentialOptions), GetHash(secret));
+ }
+ else if (tokenCredentialKey._tokenCredentialType == typeof(ClientSecretCredential))
+ {
+ return new TokenCredentialData(new ClientSecretCredential(tokenCredentialKey._audience, tokenCredentialKey._clientId, secret, tokenCredentialOptions), GetHash(secret));
+ }
+ else if (tokenCredentialKey._tokenCredentialType == typeof(WorkloadIdentityCredential))
+ {
+ // The WorkloadIdentityCredentialOptions object initialization populates its instance members
+ // from the environment variables AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_FEDERATED_TOKEN_FILE,
+ // and AZURE_ADDITIONALLY_ALLOWED_TENANTS. AZURE_CLIENT_ID may be overridden by the User Id.
+ WorkloadIdentityCredentialOptions options = new() { AuthorityHost = new Uri(tokenCredentialKey._authority) };
+
+ if (tokenCredentialKey._clientId is not null)
+ {
+ options.ClientId = tokenCredentialKey._clientId;
+ }
+
+ return new TokenCredentialData(new WorkloadIdentityCredential(options), GetHash(secret));
+ }
+
+ // This should never be reached, but if it is, throw an exception that will be noticed during development
+ throw new ArgumentException(nameof(ActiveDirectoryAuthenticationProvider));
+ }
+
+ internal class PublicClientAppKey
+ {
+ public readonly string _authority;
+ public readonly string _redirectUri;
+ public readonly string _applicationClientId;
+ #if NETFRAMEWORK
+ public readonly Func _iWin32WindowFunc;
+ #endif
+
+ public PublicClientAppKey(string authority, string redirectUri, string applicationClientId
+ #if NETFRAMEWORK
+ , Func iWin32WindowFunc
+ #endif
+ )
+ {
+ _authority = authority;
+ _redirectUri = redirectUri;
+ _applicationClientId = applicationClientId;
+ #if NETFRAMEWORK
+ _iWin32WindowFunc = iWin32WindowFunc;
+ #endif
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj != null && obj is PublicClientAppKey pcaKey)
+ {
+ return (string.CompareOrdinal(_authority, pcaKey._authority) == 0
+ && string.CompareOrdinal(_redirectUri, pcaKey._redirectUri) == 0
+ && string.CompareOrdinal(_applicationClientId, pcaKey._applicationClientId) == 0
+ #if NETFRAMEWORK
+ && pcaKey._iWin32WindowFunc == _iWin32WindowFunc
+ #endif
+ );
+ }
+ return false;
+ }
+
+ public override int GetHashCode() => Tuple.Create(_authority, _redirectUri, _applicationClientId
+ #if NETFRAMEWORK
+ , _iWin32WindowFunc
+ #endif
+ ).GetHashCode();
+ }
+
+ internal class TokenCredentialData
+ {
+ public TokenCredential _tokenCredential;
+ public byte[] _secretHash;
+
+ public TokenCredentialData(TokenCredential tokenCredential, byte[] secretHash)
+ {
+ _tokenCredential = tokenCredential;
+ _secretHash = secretHash;
+ }
+ }
+
+ internal class TokenCredentialKey
+ {
+ public readonly Type _tokenCredentialType;
+ public readonly string _authority;
+ public readonly string _scope;
+ public readonly string _audience;
+ public readonly string? _clientId;
+
+ public TokenCredentialKey(Type tokenCredentialType, string authority, string scope, string audience, string? clientId)
+ {
+ _tokenCredentialType = tokenCredentialType;
+ _authority = authority;
+ _scope = scope;
+ _audience = audience;
+ _clientId = clientId;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj != null && obj is TokenCredentialKey tcKey)
+ {
+ return string.CompareOrdinal(nameof(_tokenCredentialType), nameof(tcKey._tokenCredentialType)) == 0
+ && string.CompareOrdinal(_authority, tcKey._authority) == 0
+ && string.CompareOrdinal(_scope, tcKey._scope) == 0
+ && string.CompareOrdinal(_audience, tcKey._audience) == 0
+ && string.CompareOrdinal(_clientId, tcKey._clientId) == 0
+ ;
+ }
+ return false;
+ }
+
+ public override int GetHashCode() => Tuple.Create(_tokenCredentialType, _authority, _scope, _audience, _clientId).GetHashCode();
+ }
+
+}
+
+internal class SqlClientLogger
+{
+ public void LogInfo(string type, string method, string message)
+ {
+ SqlClientEventSource.Log.TryTraceEvent(
+ "{3}", type, method, LogLevel.Info, message);
+ }
+}
+
+internal class SqlClientEventSource
+{
+ internal class Logger
+ {
+ public void TryTraceEvent(string message, params object?[] args)
+ {
+ }
+ }
+
+ public static readonly Logger Log = new();
+}
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Azure/src/AuthenticationException.cs b/src/Microsoft.Data.SqlClient.Extensions/Azure/src/AuthenticationException.cs
new file mode 100644
index 0000000000..8676cf7566
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient.Extensions/Azure/src/AuthenticationException.cs
@@ -0,0 +1,51 @@
+// 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;
+
+///
+/// This exception is used internally by authentication providers to signal
+/// authentication failures. It is not exposed publicly.
+///
+internal class AuthenticationException : SqlAuthenticationProviderException
+{
+ ///
+ /// Construct with just a method and message. Other properties are set to
+ /// defaults per the base class.
+ ///
+ /// The authentication method.
+ /// The error message.
+ internal AuthenticationException(
+ SqlAuthenticationMethod method,
+ string message)
+ : base($"Failed to acquire access token for {method}: {message}", null)
+ {
+ }
+
+ ///
+ /// Construct with all properties specified. See the base class for details.
+ ///
+ /// The authentication method.
+ /// The failure code.
+ /// Whether the operation should be retried.
+ /// The retry period.
+ /// The error message.
+ /// The exception that caused this error.
+ internal AuthenticationException(
+ SqlAuthenticationMethod method,
+ string failureCode,
+ bool shouldRetry,
+ int retryPeriod,
+ string message,
+ Exception? causedBy = null)
+ : base(
+ method,
+ failureCode,
+ shouldRetry,
+ retryPeriod,
+ $"Failed to acquire access token for {method}: {message}",
+ causedBy)
+ {
+ }
+}
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Azure/src/Azure.csproj b/src/Microsoft.Data.SqlClient.Extensions/Azure/src/Azure.csproj
new file mode 100644
index 0000000000..74dba18d1f
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient.Extensions/Azure/src/Azure.csproj
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+ netstandard2.0
+
+
+
+
+ enable
+ enable
+
+
+
+
+ Microsoft.Data.SqlClient.Extensions.Azure
+
+
+
+
+
+ $(AzureDefaultMajorVersion).0.0.0
+
+ $(AzureAssemblyFileVersion)
+ $(AzureAssemblyFileVersion)
+ $(AzurePackageVersion)
+
+ $(Artifacts)/doc/$(TargetFramework)/$(AssemblyName).xml
+
+
+
+
+ <_Parameter1>true
+
+
+
+
+
+
+
+
+
+ $(AssemblyName)
+ $(AbstractionsPackageVersion)
+ $(PackagesDir)
+ true
+ snupkg
+
+ Microsoft Corporation
+ Microsoft Corporation
+ Microsoft.Data.SqlClient Extensions Azure
+ https://github.com/dotnet/SqlClient
+ MIT
+ dotnet.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+ $(AssemblyName)
+
+
+
+
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Azure/src/AzureVersions.props b/src/Microsoft.Data.SqlClient.Extensions/Azure/src/AzureVersions.props
new file mode 100644
index 0000000000..8c9d16969e
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient.Extensions/Azure/src/AzureVersions.props
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+ <_OurPackageVersion Condition="'$(AzurePackageVersion)' != ''">$(AzurePackageVersion)
+ <_OurPackageVersion Condition="'$(AzurePackageVersion)' == ''">$(AzureDefaultMajorVersion).0.0.$(BuildNumber)-dev
+
+
+
+ <_OurAssemblyFileVersion Condition="'$(AzureAssemblyFileVersion)' != ''">$(AzureAssemblyFileVersion)
+
+ <_OurAssemblyFileVersion Condition="'$(AzureAssemblyFileVersion)' == '' and '$(AzurePackageVersion)' != ''">$(AzurePackageVersion.Split('-')[0])
+
+ <_OurAssemblyFileVersion Condition="'$(AzureAssemblyFileVersion)' == '' and '$(AzurePackageVersion)' == ''">$(AzureDefaultMajorVersion).0.0.$(BuildNumber)
+
+
+ $(_OurPackageVersion)
+ $(_OurAssemblyFileVersion)
+
+
+
+
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Azure.Test.csproj b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Azure.Test.csproj
new file mode 100644
index 0000000000..ad94e9b4b1
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Azure.Test.csproj
@@ -0,0 +1,27 @@
+
+
+
+ net462;net8.0;net9.0
+ enable
+ enable
+ false
+ true
+ Microsoft.Data.SqlClient.Extensions.Azure.Test
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Microsoft.Data.SqlClient.sln b/src/Microsoft.Data.SqlClient.sln
index 9de2a7feaa..490d5339ce 100644
--- a/src/Microsoft.Data.SqlClient.sln
+++ b/src/Microsoft.Data.SqlClient.sln
@@ -1,4 +1,4 @@
-
+
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36203.30
@@ -328,6 +328,15 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Abstractions.Test", "Microsoft.Data.SqlClient.Extensions\Abstractions\test\Abstractions.Test.csproj", "{04ACBF75-CFF2-41AB-B652-776BC0533490}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Data.SqlClient.Samples", "..\doc\Samples\Microsoft.Data.SqlClient.Samples.csproj", "{C09B9D2F-E463-BEBD-34E4-E8F2C201A277}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Azure", "Azure", "{A20114E1-82D8-903A-C389-726EB4FD943F}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{0D2F834B-6D91-18D0-3F09-672D448751BD}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure", "Microsoft.Data.SqlClient.Extensions\Azure\src\Azure.csproj", "{20C16035-7293-45AC-8217-9B86A389E571}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{5AF52CDD-DF78-3712-7516-5B49F94F9491}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.Test", "Microsoft.Data.SqlClient.Extensions\Azure\test\Azure.Test.csproj", "{A7C0B6C7-A4B2-43CA-921B-D4FAEE86ACBC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -693,6 +702,30 @@ Global
{04ACBF75-CFF2-41AB-B652-776BC0533490}.Release|x64.Build.0 = Release|Any CPU
{04ACBF75-CFF2-41AB-B652-776BC0533490}.Release|x86.ActiveCfg = Release|Any CPU
{04ACBF75-CFF2-41AB-B652-776BC0533490}.Release|x86.Build.0 = Release|Any CPU
+ {20C16035-7293-45AC-8217-9B86A389E571}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {20C16035-7293-45AC-8217-9B86A389E571}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {20C16035-7293-45AC-8217-9B86A389E571}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {20C16035-7293-45AC-8217-9B86A389E571}.Debug|x64.Build.0 = Debug|Any CPU
+ {20C16035-7293-45AC-8217-9B86A389E571}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {20C16035-7293-45AC-8217-9B86A389E571}.Debug|x86.Build.0 = Debug|Any CPU
+ {20C16035-7293-45AC-8217-9B86A389E571}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {20C16035-7293-45AC-8217-9B86A389E571}.Release|Any CPU.Build.0 = Release|Any CPU
+ {20C16035-7293-45AC-8217-9B86A389E571}.Release|x64.ActiveCfg = Release|Any CPU
+ {20C16035-7293-45AC-8217-9B86A389E571}.Release|x64.Build.0 = Release|Any CPU
+ {20C16035-7293-45AC-8217-9B86A389E571}.Release|x86.ActiveCfg = Release|Any CPU
+ {20C16035-7293-45AC-8217-9B86A389E571}.Release|x86.Build.0 = Release|Any CPU
+ {A7C0B6C7-A4B2-43CA-921B-D4FAEE86ACBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A7C0B6C7-A4B2-43CA-921B-D4FAEE86ACBC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A7C0B6C7-A4B2-43CA-921B-D4FAEE86ACBC}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {A7C0B6C7-A4B2-43CA-921B-D4FAEE86ACBC}.Debug|x64.Build.0 = Debug|Any CPU
+ {A7C0B6C7-A4B2-43CA-921B-D4FAEE86ACBC}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {A7C0B6C7-A4B2-43CA-921B-D4FAEE86ACBC}.Debug|x86.Build.0 = Debug|Any CPU
+ {A7C0B6C7-A4B2-43CA-921B-D4FAEE86ACBC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A7C0B6C7-A4B2-43CA-921B-D4FAEE86ACBC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A7C0B6C7-A4B2-43CA-921B-D4FAEE86ACBC}.Release|x64.ActiveCfg = Release|Any CPU
+ {A7C0B6C7-A4B2-43CA-921B-D4FAEE86ACBC}.Release|x64.Build.0 = Release|Any CPU
+ {A7C0B6C7-A4B2-43CA-921B-D4FAEE86ACBC}.Release|x86.ActiveCfg = Release|Any CPU
+ {A7C0B6C7-A4B2-43CA-921B-D4FAEE86ACBC}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -755,6 +788,11 @@ Global
{B21E7C94-D805-427E-928A-8DE8EA2F08CC} = {210228A5-979A-DE06-EE1F-B35C65E1583C}
{59667E4C-0BD2-9F48-FB50-9E55DD8B1011} = {556B486E-F9B0-7EA9-6A25-DA560C312761}
{04ACBF75-CFF2-41AB-B652-776BC0533490} = {59667E4C-0BD2-9F48-FB50-9E55DD8B1011}
+ {A20114E1-82D8-903A-C389-726EB4FD943F} = {19F1F1E5-3013-7660-661A-2A15F7D606C1}
+ {0D2F834B-6D91-18D0-3F09-672D448751BD} = {A20114E1-82D8-903A-C389-726EB4FD943F}
+ {20C16035-7293-45AC-8217-9B86A389E571} = {0D2F834B-6D91-18D0-3F09-672D448751BD}
+ {5AF52CDD-DF78-3712-7516-5B49F94F9491} = {A20114E1-82D8-903A-C389-726EB4FD943F}
+ {A7C0B6C7-A4B2-43CA-921B-D4FAEE86ACBC} = {5AF52CDD-DF78-3712-7516-5B49F94F9491}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {01D48116-37A2-4D33-B9EC-94793C702431}
diff --git a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj
index 8bdb0e520a..80a965f20c 100644
--- a/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj
+++ b/src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.csproj
@@ -29,14 +29,22 @@
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Microsoft.Data.SqlClient/add-ons/Directory.Build.props b/src/Microsoft.Data.SqlClient/add-ons/Directory.Build.props
index dfeb60b38c..d5bd53f012 100644
--- a/src/Microsoft.Data.SqlClient/add-ons/Directory.Build.props
+++ b/src/Microsoft.Data.SqlClient/add-ons/Directory.Build.props
@@ -8,7 +8,6 @@
$(OS)
true
true
- Project
true
$([System.IO.Path]::Combine('$(IntermediateOutputPath)','$(TargetFramework)','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)'))
diff --git a/src/Microsoft.Data.SqlClient/add-ons/Directory.Packages.props b/src/Microsoft.Data.SqlClient/add-ons/Directory.Packages.props
deleted file mode 100644
index 197a396a0c..0000000000
--- a/src/Microsoft.Data.SqlClient/add-ons/Directory.Packages.props
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs
index 0a04d047f8..6c74d2e85f 100644
--- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs
@@ -142,30 +142,6 @@ public SqlVector(System.ReadOnlyMemory memory) { }
}
namespace Microsoft.Data.SqlClient
{
- ///
- public sealed partial class ActiveDirectoryAuthenticationProvider : SqlAuthenticationProvider
- {
- ///
- public ActiveDirectoryAuthenticationProvider() { }
- ///
- public ActiveDirectoryAuthenticationProvider(string applicationClientId) { }
- ///
- public static void ClearUserTokenCache() { }
- ///
- public ActiveDirectoryAuthenticationProvider(System.Func deviceCodeFlowCallbackMethod, string applicationClientId = null) { }
- ///
- public override System.Threading.Tasks.Task AcquireTokenAsync(SqlAuthenticationParameters parameters) { throw null; }
- ///
- public void SetDeviceCodeFlowCallback(System.Func deviceCodeFlowCallbackMethod) { }
- ///
- public void SetAcquireAuthorizationCodeAsyncCallback(System.Func> acquireAuthorizationCodeAsyncCallback) { }
- ///
- public override bool IsSupported(SqlAuthenticationMethod authentication) { throw null; }
- ///
- public override void BeforeLoad(SqlAuthenticationMethod authentication) { }
- ///
- public override void BeforeUnload(SqlAuthenticationMethod authentication) { }
- }
///
public enum ApplicationIntent
{
@@ -204,85 +180,6 @@ protected SqlAuthenticationInitializer() { }
///
public abstract void Initialize();
}
- ///
- public enum SqlAuthenticationMethod
- {
- ///
- NotSpecified = 0,
- ///
- SqlPassword = 1,
- ///
- [System.Obsolete("ActiveDirectoryPassword is deprecated, use a more secure authentication method. See https://aka.ms/SqlClientEntraIDAuthentication for more details.")]
- ActiveDirectoryPassword = 2,
- ///
- ActiveDirectoryIntegrated = 3,
- ///
- ActiveDirectoryInteractive = 4,
- ///
- ActiveDirectoryServicePrincipal = 5,
- ///
- ActiveDirectoryDeviceCodeFlow = 6,
- ///
- ActiveDirectoryManagedIdentity = 7,
- ///
- ActiveDirectoryMSI = 8,
- ///
- ActiveDirectoryDefault = 9,
- ///
- ActiveDirectoryWorkloadIdentity = 10
- }
- ///
- public class SqlAuthenticationParameters
- {
- ///
- protected SqlAuthenticationParameters(Microsoft.Data.SqlClient.SqlAuthenticationMethod authenticationMethod, string serverName, string databaseName, string resource, string authority, string userId, string password, System.Guid connectionId, int connectionTimeout) { }
- ///
- public Microsoft.Data.SqlClient.SqlAuthenticationMethod AuthenticationMethod { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
- ///
- public string Authority { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
- ///
- public System.Guid ConnectionId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
- ///
- public string DatabaseName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
- ///
- public string Password { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
- ///
- public string Resource { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
- ///
- public string ServerName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
- ///
- public string UserId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
- ///
- public int ConnectionTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
- }
- ///
- public abstract partial class SqlAuthenticationProvider
- {
- ///
- protected SqlAuthenticationProvider() { }
- ///
- public abstract System.Threading.Tasks.Task AcquireTokenAsync(Microsoft.Data.SqlClient.SqlAuthenticationParameters parameters);
- ///
- public virtual void BeforeLoad(Microsoft.Data.SqlClient.SqlAuthenticationMethod authenticationMethod) { }
- ///
- public virtual void BeforeUnload(Microsoft.Data.SqlClient.SqlAuthenticationMethod authenticationMethod) { }
- ///
- public static Microsoft.Data.SqlClient.SqlAuthenticationProvider GetProvider(Microsoft.Data.SqlClient.SqlAuthenticationMethod authenticationMethod) { throw null; }
- ///
- public abstract bool IsSupported(Microsoft.Data.SqlClient.SqlAuthenticationMethod authenticationMethod);
- ///
- public static bool SetProvider(Microsoft.Data.SqlClient.SqlAuthenticationMethod authenticationMethod, Microsoft.Data.SqlClient.SqlAuthenticationProvider provider) { throw null; }
- }
- ///
- public partial class SqlAuthenticationToken
- {
- ///
- public SqlAuthenticationToken(string accessToken, System.DateTimeOffset expiresOn) { }
- ///
- public string AccessToken { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
- ///
- public System.DateTimeOffset ExpiresOn { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
- }
///
public sealed partial class SqlBulkCopy : System.IDisposable
{
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 843f8c0bfe..aaccec8c11 100644
--- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj
@@ -32,8 +32,6 @@
-
-
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 bf8c916315..6995dea555 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
@@ -647,18 +647,12 @@
Microsoft\Data\SqlClient\SqlAppContextSwitchManager.netcore.cs
-
- Microsoft\Data\SqlClient\SqlAuthenticationParameters.cs
-
-
- Microsoft\Data\SqlClient\SqlAuthenticationProvider.cs
+
+ Microsoft\Data\SqlClient\SqlAuthenticationParametersBuilder.cs
Microsoft\Data\SqlClient\SqlAuthenticationProviderManager.cs
-
- Microsoft\Data\SqlClient\SqlAuthenticationToken.cs
-
Microsoft\Data\SqlClient\SqlBatch.cs
@@ -1076,8 +1070,6 @@
-
-
diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs
index e662fc0560..30816757eb 100644
--- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs
+++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs
@@ -57,32 +57,6 @@ public sealed class SqlDataSourceEnumerator : System.Data.Common.DbDataSourceEnu
namespace Microsoft.Data.SqlClient
{
- ///
- public sealed class ActiveDirectoryAuthenticationProvider : SqlAuthenticationProvider
- {
- ///
- public ActiveDirectoryAuthenticationProvider() { }
- ///
- public ActiveDirectoryAuthenticationProvider(string applicationClientId) { }
- ///
- public static void ClearUserTokenCache() { }
- ///
- public ActiveDirectoryAuthenticationProvider(System.Func deviceCodeFlowCallbackMethod, string applicationClientId = null) { }
- ///
- public override System.Threading.Tasks.Task AcquireTokenAsync(SqlAuthenticationParameters parameters) { throw null; }
- ///
- public void SetDeviceCodeFlowCallback(System.Func deviceCodeFlowCallbackMethod) { }
- ///
- public void SetAcquireAuthorizationCodeAsyncCallback(System.Func> acquireAuthorizationCodeAsyncCallback) { }
- ///
- public void SetIWin32WindowFunc(System.Func iWin32WindowFunc) { }
- ///
- public override bool IsSupported(SqlAuthenticationMethod authentication) { throw null; }
- ///
- public override void BeforeLoad(SqlAuthenticationMethod authentication) { }
- ///
- public override void BeforeUnload(SqlAuthenticationMethod authentication) { }
- }
///
public enum ApplicationIntent
{
@@ -122,85 +96,6 @@ protected SqlAuthenticationInitializer() { }
///
public abstract void Initialize();
}
- ///
- public enum SqlAuthenticationMethod
- {
- ///
- NotSpecified = 0,
- ///
- SqlPassword = 1,
- ///
- [System.ObsoleteAttribute("ActiveDirectoryPassword is deprecated, use a more secure authentication method. See https://aka.ms/SqlClientEntraIDAuthentication for more details.")]
- ActiveDirectoryPassword = 2,
- ///
- ActiveDirectoryIntegrated = 3,
- ///
- ActiveDirectoryInteractive = 4,
- ///
- ActiveDirectoryServicePrincipal = 5,
- ///
- ActiveDirectoryDeviceCodeFlow = 6,
- ///
- ActiveDirectoryManagedIdentity = 7,
- ///
- ActiveDirectoryMSI = 8,
- ///
- ActiveDirectoryDefault = 9,
- ///
- ActiveDirectoryWorkloadIdentity = 10
- }
- ///
- public class SqlAuthenticationParameters
- {
- ///
- protected SqlAuthenticationParameters(Microsoft.Data.SqlClient.SqlAuthenticationMethod authenticationMethod, string serverName, string databaseName, string resource, string authority, string userId, string password, System.Guid connectionId, int connectionTimeout) { }
- ///
- public Microsoft.Data.SqlClient.SqlAuthenticationMethod AuthenticationMethod { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
- ///
- public string Authority { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
- ///
- public System.Guid ConnectionId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
- ///
- public string DatabaseName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
- ///
- public string Password { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
- ///
- public string Resource { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
- ///
- public string ServerName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
- ///
- public string UserId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
- ///
- public int ConnectionTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
- }
- ///
- public abstract partial class SqlAuthenticationProvider
- {
- ///
- protected SqlAuthenticationProvider() { }
- ///
- public abstract System.Threading.Tasks.Task AcquireTokenAsync(Microsoft.Data.SqlClient.SqlAuthenticationParameters parameters);
- ///
- public virtual void BeforeLoad(Microsoft.Data.SqlClient.SqlAuthenticationMethod authenticationMethod) { }
- ///
- public virtual void BeforeUnload(Microsoft.Data.SqlClient.SqlAuthenticationMethod authenticationMethod) { }
- ///
- public static Microsoft.Data.SqlClient.SqlAuthenticationProvider GetProvider(Microsoft.Data.SqlClient.SqlAuthenticationMethod authenticationMethod) { throw null; }
- ///
- public abstract bool IsSupported(Microsoft.Data.SqlClient.SqlAuthenticationMethod authenticationMethod);
- ///
- public static bool SetProvider(Microsoft.Data.SqlClient.SqlAuthenticationMethod authenticationMethod, Microsoft.Data.SqlClient.SqlAuthenticationProvider provider) { throw null; }
- }
- ///
- public partial class SqlAuthenticationToken
- {
- ///
- public SqlAuthenticationToken(string accessToken, System.DateTimeOffset expiresOn) { }
- ///
- public string AccessToken { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
- ///
- public System.DateTimeOffset ExpiresOn { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
- }
///
public sealed partial class SqlBulkCopy : System.IDisposable
{
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 6ab452e0c2..29fbddc3b6 100644
--- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj
@@ -32,8 +32,6 @@
-
-
All
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 a4cc2aa55a..804b0aea4f 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
@@ -645,18 +645,12 @@
Microsoft\Data\SqlClient\SqlAeadAes256CbcHmac256Factory.cs
-
- Microsoft\Data\SqlClient\SqlAuthenticationParameters.cs
-
-
- Microsoft\Data\SqlClient\SqlAuthenticationProvider.cs
+
+ Microsoft\Data\SqlClient\SqlAuthenticationParametersBuilder.cs
Microsoft\Data\SqlClient\SqlAuthenticationProviderManager.cs
-
- Microsoft\Data\SqlClient\SqlAuthenticationToken.cs
-
Microsoft\Data\SqlClient\SqlBatchCommand.cs
@@ -1059,8 +1053,6 @@
-
-
All
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj
index 9bfceb3dc0..3403d2efd6 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj
@@ -69,8 +69,6 @@
-
-
All
@@ -91,8 +89,6 @@
-
-
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs
index abbeeda0eb..b3fe5ebd8b 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs
@@ -21,7 +21,6 @@
using Microsoft.Data.Common.ConnectionString;
using Microsoft.Data.SqlClient;
using Microsoft.Data.SqlClient.Connection;
-using Microsoft.Identity.Client;
using Microsoft.SqlServer.Server;
using IsolationLevel = System.Data.IsolationLevel;
@@ -501,11 +500,15 @@ internal static ArgumentException InvalidArgumentLength(string argumentName, int
internal static ArgumentException MustBeReadOnly(string argumentName) => Argument(StringsHelper.GetString(Strings.ADP_MustBeReadOnly, argumentName));
+<<<<<<< HEAD
internal static Exception CreateSqlException(
MsalException msalException,
SqlConnectionString connectionOptions,
SqlConnectionInternal sender,
string username)
+=======
+ internal static Exception CreateSqlException(SqlAuthenticationProviderException authException, SqlConnectionString connectionOptions, SqlInternalConnectionTds sender, string username)
+>>>>>>> 4e2c6b4a (Move AAD/Entra Authentication into new Azure package (#3680))
{
// Error[0]
SqlErrorCollection sqlErs = new();
@@ -513,20 +516,20 @@ internal static Exception CreateSqlException(
sqlErs.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS,
connectionOptions.DataSource,
StringsHelper.GetString(Strings.SQL_MSALFailure, username, connectionOptions.Authentication.ToString("G")),
- ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, 0));
+ authException.Method.ToString(), 0));
// Error[1]
- string errorMessage1 = StringsHelper.GetString(Strings.SQL_MSALInnerException, msalException.ErrorCode);
+ string errorMessage1 = StringsHelper.GetString(Strings.SQL_MSALInnerException, authException.FailureCode);
sqlErs.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS,
- connectionOptions.DataSource, errorMessage1,
- ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, 0));
+ connectionOptions.DataSource, errorMessage1,
+ authException.Method.ToString(), 0));
// Error[2]
- if (!string.IsNullOrEmpty(msalException.Message))
+ if (!string.IsNullOrEmpty(authException.Message))
{
sqlErs.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS,
- connectionOptions.DataSource, msalException.Message,
- ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, 0));
+ connectionOptions.DataSource, authException.Message,
+ authException.Method.ToString(), 0));
}
return SqlException.CreateException(sqlErs, "", sender, innerException: null, batchCommand: null);
}
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs
deleted file mode 100644
index 0393de9beb..0000000000
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationProvider.cs
+++ /dev/null
@@ -1,743 +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 System;
-using System.Collections.Concurrent;
-using System.Security.Cryptography;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using Azure.Core;
-using Azure.Identity;
-using Microsoft.Data.Common.ConnectionString;
-using Microsoft.Extensions.Caching.Memory;
-using Microsoft.Identity.Client;
-using Microsoft.Identity.Client.Extensibility;
-
-namespace Microsoft.Data.SqlClient
-{
- ///
- public sealed class ActiveDirectoryAuthenticationProvider : SqlAuthenticationProvider
- {
- ///
- /// This is a static cache instance meant to hold instances of "PublicClientApplication" mapping to information available in PublicClientAppKey.
- /// The purpose of this cache is to allow re-use of Access Tokens fetched for a user interactively or with any other mode
- /// to avoid interactive authentication request every-time, within application scope making use of MSAL's userTokenCache.
- ///
- private static readonly ConcurrentDictionary s_pcaMap = new();
- private static readonly ConcurrentDictionary s_tokenCredentialMap = new();
- private static SemaphoreSlim s_pcaMapModifierSemaphore = new(1, 1);
- private static SemaphoreSlim s_tokenCredentialMapModifierSemaphore = new(1, 1);
- private static readonly MemoryCache s_accountPwCache = new MemoryCache(new MemoryCacheOptions());
- private static readonly int s_accountPwCacheTtlInHours = 2;
- private static readonly string s_nativeClientRedirectUri = "https://login.microsoftonline.com/common/oauth2/nativeclient";
- private static readonly string s_defaultScopeSuffix = "/.default";
- private readonly string _type = typeof(ActiveDirectoryAuthenticationProvider).Name;
- private readonly SqlClientLogger _logger = new();
- private Func _deviceCodeFlowCallback;
- private ICustomWebUi _customWebUI = null;
- private readonly string _applicationClientId = ActiveDirectoryAuthentication.AdoClientId;
-
- ///
- public ActiveDirectoryAuthenticationProvider()
- : this(DefaultDeviceFlowCallback)
- {
- }
-
- ///
- public ActiveDirectoryAuthenticationProvider(string applicationClientId)
- : this(DefaultDeviceFlowCallback, applicationClientId)
- {
- }
-
- ///
- public ActiveDirectoryAuthenticationProvider(Func deviceCodeFlowCallbackMethod, string applicationClientId = null)
- {
- if (applicationClientId != null)
- {
- _applicationClientId = applicationClientId;
- }
- SetDeviceCodeFlowCallback(deviceCodeFlowCallbackMethod);
- }
-
- ///
- public static void ClearUserTokenCache()
- {
- if (!s_pcaMap.IsEmpty)
- {
- s_pcaMap.Clear();
- }
-
- if (!s_tokenCredentialMap.IsEmpty)
- {
- s_tokenCredentialMap.Clear();
- }
- }
-
- ///
- public void SetDeviceCodeFlowCallback(Func deviceCodeFlowCallbackMethod) => _deviceCodeFlowCallback = deviceCodeFlowCallbackMethod;
-
- ///
- public void SetAcquireAuthorizationCodeAsyncCallback(Func> acquireAuthorizationCodeAsyncCallback) => _customWebUI = new CustomWebUi(acquireAuthorizationCodeAsyncCallback);
-
- ///
- public override bool IsSupported(SqlAuthenticationMethod authentication)
- {
- return authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated
- #pragma warning disable 0618 // Type or member is obsolete
- || authentication == SqlAuthenticationMethod.ActiveDirectoryPassword
- #pragma warning restore 0618 // Type or member is obsolete
- || authentication == SqlAuthenticationMethod.ActiveDirectoryInteractive
- || authentication == SqlAuthenticationMethod.ActiveDirectoryServicePrincipal
- || authentication == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow
- || authentication == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity
- || authentication == SqlAuthenticationMethod.ActiveDirectoryMSI
- || authentication == SqlAuthenticationMethod.ActiveDirectoryDefault
- || authentication == SqlAuthenticationMethod.ActiveDirectoryWorkloadIdentity;
- }
-
- ///
- public override void BeforeLoad(SqlAuthenticationMethod authentication)
- {
- _logger.LogInfo(_type, "BeforeLoad", $"being loaded into SqlAuthProviders for {authentication}.");
- }
-
- ///
- public override void BeforeUnload(SqlAuthenticationMethod authentication)
- {
- _logger.LogInfo(_type, "BeforeUnload", $"being unloaded from SqlAuthProviders for {authentication}.");
- }
-
-#if NETFRAMEWORK
- private Func _iWin32WindowFunc = null;
-
- ///
- public void SetIWin32WindowFunc(Func iWin32WindowFunc) => this._iWin32WindowFunc = iWin32WindowFunc;
-#endif
-
- ///
-
- public override async Task AcquireTokenAsync(SqlAuthenticationParameters parameters)
- {
- using CancellationTokenSource cts = new();
-
- // Use Connection timeout value to cancel token acquire request after certain period of time.
- int timeout = parameters.ConnectionTimeout * 1000; // Convert to milliseconds
- if (timeout > 0) // if ConnectionTimeout is 0 or the millis overflows an int, no need to set CancelAfter
- {
- cts.CancelAfter(timeout);
- }
-
- string scope = parameters.Resource.EndsWith(s_defaultScopeSuffix, StringComparison.Ordinal) ? parameters.Resource : parameters.Resource + s_defaultScopeSuffix;
- string[] scopes = new string[] { scope };
- TokenRequestContext tokenRequestContext = new(scopes);
-
- /* We split audience from Authority URL here. Audience can be one of the following:
- * The Azure AD authority audience enumeration
- * The tenant ID, which can be:
- * - A GUID (the ID of your Azure AD instance), for single-tenant applications
- * - A domain name associated with your Azure AD instance (also for single-tenant applications)
- * One of these placeholders as a tenant ID in place of the Azure AD authority audience enumeration:
- * - `organizations` for a multitenant application
- * - `consumers` to sign in users only with their personal accounts
- * - `common` to sign in users with their work and school accounts or their personal Microsoft accounts
- *
- * MSAL will throw a meaningful exception if you specify both the Azure AD authority audience and the tenant ID.
- * If you don't specify an audience, your app will target Azure AD and personal Microsoft accounts as an audience. (That is, it will behave as though `common` were specified.)
- * More information: https://docs.microsoft.com/azure/active-directory/develop/msal-client-application-configuration
- **/
-
- int separatorIndex = parameters.Authority.LastIndexOf('/');
- string authority = parameters.Authority.Remove(separatorIndex + 1);
- string audience = parameters.Authority.Substring(separatorIndex + 1);
- string clientId = string.IsNullOrWhiteSpace(parameters.UserId) ? null : parameters.UserId;
-
- if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryDefault)
- {
- // Cache DefaultAzureCredenial based on scope, authority, audience, and clientId
- TokenCredentialKey tokenCredentialKey = new(typeof(DefaultAzureCredential), authority, scope, audience, clientId);
- AccessToken accessToken = await GetTokenAsync(tokenCredentialKey, string.Empty, tokenRequestContext, cts.Token).ConfigureAwait(false);
- SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Default auth mode. Expiry Time: {0}", accessToken.ExpiresOn);
- return new SqlAuthenticationToken(accessToken.Token, accessToken.ExpiresOn);
- }
-
- TokenCredentialOptions tokenCredentialOptions = new() { AuthorityHost = new Uri(authority) };
-
- if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryMSI)
- {
- // Cache ManagedIdentityCredential based on scope, authority, and clientId
- TokenCredentialKey tokenCredentialKey = new(typeof(ManagedIdentityCredential), authority, scope, string.Empty, clientId);
- AccessToken accessToken = await GetTokenAsync(tokenCredentialKey, string.Empty, tokenRequestContext, cts.Token).ConfigureAwait(false);
- SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Managed Identity auth mode. Expiry Time: {0}", accessToken.ExpiresOn);
- return new SqlAuthenticationToken(accessToken.Token, accessToken.ExpiresOn);
- }
-
- if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryServicePrincipal)
- {
- // Cache ClientSecretCredential based on scope, authority, audience, and clientId
- TokenCredentialKey tokenCredentialKey = new(typeof(ClientSecretCredential), authority, scope, audience, clientId);
- AccessToken accessToken = await GetTokenAsync(tokenCredentialKey, parameters.Password, tokenRequestContext, cts.Token).ConfigureAwait(false);
- SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Active Directory Service Principal auth mode. Expiry Time: {0}", accessToken.ExpiresOn);
- return new SqlAuthenticationToken(accessToken.Token, accessToken.ExpiresOn);
- }
-
- if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryWorkloadIdentity)
- {
- // Cache WorkloadIdentityCredential based on authority and clientId
- TokenCredentialKey tokenCredentialKey = new(typeof(WorkloadIdentityCredential), authority, string.Empty, string.Empty, clientId);
- // If either tenant id, client id, or the token file path are not specified when fetching the token,
- // a CredentialUnavailableException will be thrown instead
- AccessToken accessToken = await GetTokenAsync(tokenCredentialKey, string.Empty, tokenRequestContext, cts.Token).ConfigureAwait(false);
- SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Workload Identity auth mode. Expiry Time: {0}", accessToken.ExpiresOn);
- return new SqlAuthenticationToken(accessToken.Token, accessToken.ExpiresOn);
- }
-
- /*
- * Today, MSAL.NET uses another redirect URI by default in desktop applications that run on Windows
- * (urn:ietf:wg:oauth:2.0:oob). In the future, we'll want to change this default, so we recommend
- * that you use https://login.microsoftonline.com/common/oauth2/nativeclient.
- *
- * https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-desktop-app-registration#redirect-uris
- */
- string redirectUri = s_nativeClientRedirectUri;
-
-#if NET
- if (parameters.AuthenticationMethod != SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow)
- {
- redirectUri = "http://localhost";
- }
-#endif
- PublicClientAppKey pcaKey = new(parameters.Authority, redirectUri, _applicationClientId
-#if NETFRAMEWORK
- , _iWin32WindowFunc
-#endif
- );
-
- AuthenticationResult result = null;
- IPublicClientApplication app = await GetPublicClientAppInstanceAsync(pcaKey, cts.Token).ConfigureAwait(false);
-
- if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryIntegrated)
- {
- result = await TryAcquireTokenSilent(app, parameters, scopes, cts).ConfigureAwait(false);
-
- if (result == null)
- {
- if (!string.IsNullOrEmpty(parameters.UserId))
- {
- // The AcquireTokenByIntegratedWindowsAuth method is marked as obsolete in MSAL.NET
- // but it is still a supported way to acquire tokens for Active Directory Integrated authentication.
-#pragma warning disable CS0618 // Type or member is obsolete
- result = await app.AcquireTokenByIntegratedWindowsAuth(scopes)
-#pragma warning restore CS0618 // Type or member is obsolete
- .WithCorrelationId(parameters.ConnectionId)
- .WithUsername(parameters.UserId)
- .ExecuteAsync(cancellationToken: cts.Token)
- .ConfigureAwait(false);
- }
- else
- {
-#pragma warning disable CS0618 // Type or member is obsolete
- result = await app.AcquireTokenByIntegratedWindowsAuth(scopes)
-#pragma warning restore CS0618 // Type or member is obsolete
- .WithCorrelationId(parameters.ConnectionId)
- .ExecuteAsync(cancellationToken: cts.Token)
- .ConfigureAwait(false);
- }
- SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Active Directory Integrated auth mode. Expiry Time: {0}", result?.ExpiresOn);
- }
- }
- #pragma warning disable 0618 // Type or member is obsolete
- else if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryPassword)
- #pragma warning restore 0618 // Type or member is obsolete
- {
- string pwCacheKey = GetAccountPwCacheKey(parameters);
- object previousPw = s_accountPwCache.Get(pwCacheKey);
- byte[] currPwHash = GetHash(parameters.Password);
-
- if (previousPw != null &&
- previousPw is byte[] previousPwBytes &&
- // Only get the cached token if the current password hash matches the previously used password hash
- AreEqual(currPwHash, previousPwBytes))
- {
- result = await TryAcquireTokenSilent(app, parameters, scopes, cts).ConfigureAwait(false);
- }
-
- if (result == null)
- {
-#pragma warning disable CS0618 // Type or member is obsolete
- result = await app.AcquireTokenByUsernamePassword(scopes, parameters.UserId, parameters.Password)
-#pragma warning restore CS0618 // Type or member is obsolete
- .WithCorrelationId(parameters.ConnectionId)
- .ExecuteAsync(cancellationToken: cts.Token)
- .ConfigureAwait(false);
-
- // We cache the password hash to ensure future connection requests include a validated password
- // when we check for a cached MSAL account. Otherwise, a connection request with the same username
- // against the same tenant could succeed with an invalid password when we re-use the cached token.
- using (ICacheEntry entry = s_accountPwCache.CreateEntry(pwCacheKey))
- {
- entry.Value = GetHash(parameters.Password);
- entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(s_accountPwCacheTtlInHours);
- }
- SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Active Directory Password auth mode. Expiry Time: {0}", result?.ExpiresOn);
- }
- }
- else if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryInteractive ||
- parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow)
- {
- try
- {
- result = await TryAcquireTokenSilent(app, parameters, scopes, cts).ConfigureAwait(false);
- SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (silent) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, result?.ExpiresOn);
- }
- catch (MsalUiRequiredException)
- {
- // An 'MsalUiRequiredException' is thrown in the case where an interaction is required with the end user of the application,
- // for instance, if no refresh token was in the cache, or the user needs to consent, or re-sign-in (for instance if the password expired),
- // or the user needs to perform two factor authentication.
- result = await AcquireTokenInteractiveDeviceFlowAsync(app, scopes, parameters.ConnectionId, parameters.UserId, parameters.AuthenticationMethod, cts, _customWebUI, _deviceCodeFlowCallback).ConfigureAwait(false);
- SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (interactive) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, result?.ExpiresOn);
- }
-
- if (result == null)
- {
- // If no existing 'account' is found, we request user to sign in interactively.
- result = await AcquireTokenInteractiveDeviceFlowAsync(app, scopes, parameters.ConnectionId, parameters.UserId, parameters.AuthenticationMethod, cts, _customWebUI, _deviceCodeFlowCallback).ConfigureAwait(false);
- SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (interactive) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, result?.ExpiresOn);
- }
- }
- else
- {
- SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | {0} authentication mode not supported by ActiveDirectoryAuthenticationProvider class.", parameters.AuthenticationMethod);
- throw SQL.UnsupportedAuthenticationSpecified(parameters.AuthenticationMethod);
- }
-
- return new SqlAuthenticationToken(result.AccessToken, result.ExpiresOn);
- }
-
- private static async Task TryAcquireTokenSilent(IPublicClientApplication app, SqlAuthenticationParameters parameters,
- string[] scopes, CancellationTokenSource cts)
- {
- AuthenticationResult result = null;
-
- // Fetch available accounts from 'app' instance
- System.Collections.Generic.IEnumerator accounts = (await app.GetAccountsAsync().ConfigureAwait(false)).GetEnumerator();
-
- IAccount account = default;
- if (accounts.MoveNext())
- {
- if (!string.IsNullOrEmpty(parameters.UserId))
- {
- do
- {
- IAccount currentVal = accounts.Current;
- if (string.Compare(parameters.UserId, currentVal.Username, StringComparison.InvariantCultureIgnoreCase) == 0)
- {
- account = currentVal;
- break;
- }
- }
- while (accounts.MoveNext());
- }
- else
- {
- account = accounts.Current;
- }
- }
-
- if (account != null)
- {
- // If 'account' is available in 'app', we use the same to acquire token silently.
- // Read More on API docs: https://docs.microsoft.com/dotnet/api/microsoft.identity.client.clientapplicationbase.acquiretokensilent
- result = await app.AcquireTokenSilent(scopes, account).ExecuteAsync(cancellationToken: cts.Token).ConfigureAwait(false);
- SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (silent) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, result?.ExpiresOn);
- }
-
- return result;
- }
-
- private static async Task AcquireTokenInteractiveDeviceFlowAsync(IPublicClientApplication app, string[] scopes, Guid connectionId, string userId,
- SqlAuthenticationMethod authenticationMethod, CancellationTokenSource cts, ICustomWebUi customWebUI, Func deviceCodeFlowCallback)
- {
- try
- {
- if (authenticationMethod == SqlAuthenticationMethod.ActiveDirectoryInteractive)
- {
- CancellationTokenSource ctsInteractive = new();
-#if NET
- /*
- * On .NET Core, MSAL will start the system browser as a separate process. MSAL does not have control over this browser,
- * but once the user finishes authentication, the web page is redirected in such a way that MSAL can intercept the Uri.
- * MSAL cannot detect if the user navigates away or simply closes the browser. Apps using this technique are encouraged
- * to define a timeout (via CancellationToken). We recommend a timeout of at least a few minutes, to take into account
- * cases where the user is prompted to change password or perform 2FA.
- *
- * https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/System-Browser-on-.Net-Core#system-browser-experience
- */
- ctsInteractive.CancelAfter(180000);
-#endif
- if (customWebUI != null)
- {
- return await app.AcquireTokenInteractive(scopes)
- .WithCorrelationId(connectionId)
- .WithCustomWebUi(customWebUI)
- .WithLoginHint(userId)
- .ExecuteAsync(ctsInteractive.Token)
- .ConfigureAwait(false);
- }
- else
- {
- /*
- * We will use the MSAL Embedded or System web browser which changes by Default in MSAL according to this table:
- *
- * Framework Embedded System Default
- * -------------------------------------------
- * .NET Classic Yes Yes^ Embedded
- * .NET Core No Yes^ System
- * .NET Standard No No NONE
- * UWP Yes No Embedded
- * Xamarin.Android Yes Yes System
- * Xamarin.iOS Yes Yes System
- * Xamarin.Mac Yes No Embedded
- *
- * ^ Requires "http://localhost" redirect URI
- *
- * https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/MSAL.NET-uses-web-browser#at-a-glance
- */
- return await app.AcquireTokenInteractive(scopes)
- .WithCorrelationId(connectionId)
- .WithLoginHint(userId)
- .ExecuteAsync(ctsInteractive.Token)
- .ConfigureAwait(false);
- }
- }
- else
- {
- AuthenticationResult result = await app.AcquireTokenWithDeviceCode(scopes,
- deviceCodeResult => deviceCodeFlowCallback(deviceCodeResult))
- .WithCorrelationId(connectionId)
- .ExecuteAsync(cancellationToken: cts.Token)
- .ConfigureAwait(false);
- return result;
- }
- }
- catch (OperationCanceledException)
- {
- SqlClientEventSource.Log.TryTraceEvent("AcquireTokenInteractiveDeviceFlowAsync | Operation timed out while acquiring access token.");
- throw (authenticationMethod == SqlAuthenticationMethod.ActiveDirectoryInteractive) ?
- SQL.ActiveDirectoryInteractiveTimeout() :
- SQL.ActiveDirectoryDeviceFlowTimeout();
- }
- }
-
- private static Task DefaultDeviceFlowCallback(DeviceCodeResult result)
- {
- // This will print the message on the console which tells the user where to go sign-in using
- // a separate browser and the code to enter once they sign in.
- // The AcquireTokenWithDeviceCode() method will poll the server after firing this
- // device code callback to look for the successful login of the user via that browser.
- // This background polling (whose interval and timeout data is also provided as fields in the
- // deviceCodeCallback class) will occur until:
- // * The user has successfully logged in via browser and entered the proper code
- // * The timeout specified by the server for the lifetime of this code (typically ~15 minutes) has been reached
- // * The developing application calls the Cancel() method on a CancellationToken sent into the method.
- // If this occurs, an OperationCanceledException will be thrown (see catch below for more details).
- SqlClientEventSource.Log.TryTraceEvent("AcquireTokenInteractiveDeviceFlowAsync | Callback triggered with Device Code Result: {0}", result.Message);
- Console.WriteLine(result.Message);
- return Task.FromResult(0);
- }
-
- private class CustomWebUi : ICustomWebUi
- {
- private readonly Func> _acquireAuthorizationCodeAsyncCallback;
-
- internal CustomWebUi(Func> acquireAuthorizationCodeAsyncCallback) => _acquireAuthorizationCodeAsyncCallback = acquireAuthorizationCodeAsyncCallback;
-
- public Task AcquireAuthorizationCodeAsync(Uri authorizationUri, Uri redirectUri, CancellationToken cancellationToken)
- => _acquireAuthorizationCodeAsyncCallback.Invoke(authorizationUri, redirectUri, cancellationToken);
- }
-
- private async Task GetPublicClientAppInstanceAsync(PublicClientAppKey publicClientAppKey, CancellationToken cancellationToken)
- {
- if (!s_pcaMap.TryGetValue(publicClientAppKey, out IPublicClientApplication clientApplicationInstance))
- {
- await s_pcaMapModifierSemaphore.WaitAsync(cancellationToken);
- try
- {
- // Double-check in case another thread added it while we waited for the semaphore
- if (!s_pcaMap.TryGetValue(publicClientAppKey, out clientApplicationInstance))
- {
- clientApplicationInstance = CreateClientAppInstance(publicClientAppKey);
- s_pcaMap.TryAdd(publicClientAppKey, clientApplicationInstance);
- }
- }
- finally
- {
- s_pcaMapModifierSemaphore.Release();
- }
- }
-
- return clientApplicationInstance;
- }
-
- private static async Task GetTokenAsync(TokenCredentialKey tokenCredentialKey, string secret,
- TokenRequestContext tokenRequestContext, CancellationToken cancellationToken)
- {
- if (!s_tokenCredentialMap.TryGetValue(tokenCredentialKey, out TokenCredentialData tokenCredentialInstance))
- {
- await s_tokenCredentialMapModifierSemaphore.WaitAsync(cancellationToken);
- try
- {
- // Double-check in case another thread added it while we waited for the semaphore
- if (!s_tokenCredentialMap.TryGetValue(tokenCredentialKey, out tokenCredentialInstance))
- {
- tokenCredentialInstance = CreateTokenCredentialInstance(tokenCredentialKey, secret);
- s_tokenCredentialMap.TryAdd(tokenCredentialKey, tokenCredentialInstance);
- }
- }
- finally
- {
- s_tokenCredentialMapModifierSemaphore.Release();
- }
- }
-
- if (!AreEqual(tokenCredentialInstance._secretHash, GetHash(secret)))
- {
- // If the secret hash has changed, we need to remove the old token credential instance and create a new one.
- await s_tokenCredentialMapModifierSemaphore.WaitAsync(cancellationToken);
- try
- {
- s_tokenCredentialMap.TryRemove(tokenCredentialKey, out _);
- tokenCredentialInstance = CreateTokenCredentialInstance(tokenCredentialKey, secret);
- s_tokenCredentialMap.TryAdd(tokenCredentialKey, tokenCredentialInstance);
- }
- finally
- {
- s_tokenCredentialMapModifierSemaphore.Release();
- }
- }
-
- return await tokenCredentialInstance._tokenCredential.GetTokenAsync(tokenRequestContext, cancellationToken);
- }
-
- private static string GetAccountPwCacheKey(SqlAuthenticationParameters parameters)
- {
- return parameters.Authority + "+" + parameters.UserId;
- }
-
- private static byte[] GetHash(string input)
- {
- byte[] unhashedBytes = Encoding.Unicode.GetBytes(input);
- SHA256 sha256 = SHA256.Create();
- byte[] hashedBytes = sha256.ComputeHash(unhashedBytes);
- return hashedBytes;
- }
-
- private static bool AreEqual(byte[] a1, byte[] a2)
- {
- if (ReferenceEquals(a1, a2))
- {
- return true;
- }
- else if (a1 is null || a2 is null)
- {
- return false;
- }
- else if (a1.Length != a2.Length)
- {
- return false;
- }
-
- return a1.AsSpan().SequenceEqual(a2.AsSpan());
- }
-
- private IPublicClientApplication CreateClientAppInstance(PublicClientAppKey publicClientAppKey)
- {
- PublicClientApplicationBuilder builder = PublicClientApplicationBuilder
- .CreateWithApplicationOptions(new PublicClientApplicationOptions
- {
- ClientId = publicClientAppKey._applicationClientId,
- ClientName = DbConnectionStringDefaults.ApplicationName,
- ClientVersion = Common.ADP.GetAssemblyVersion().ToString(),
- RedirectUri = publicClientAppKey._redirectUri,
- })
- .WithAuthority(publicClientAppKey._authority);
-
- #if NETFRAMEWORK
- if (_iWin32WindowFunc is not null)
- {
- builder.WithParentActivityOrWindow(_iWin32WindowFunc);
- }
- #endif
-
- return builder.Build();
- }
-
- private static TokenCredentialData CreateTokenCredentialInstance(TokenCredentialKey tokenCredentialKey, string secret)
- {
- if (tokenCredentialKey._tokenCredentialType == typeof(DefaultAzureCredential))
- {
- DefaultAzureCredentialOptions defaultAzureCredentialOptions = new()
- {
- AuthorityHost = new Uri(tokenCredentialKey._authority),
- TenantId = tokenCredentialKey._audience,
- ExcludeInteractiveBrowserCredential = true // Force disabled, even though it's disabled by default to respect driver specifications.
- };
-
- // Optionally set clientId when available
- if (tokenCredentialKey._clientId is not null)
- {
- defaultAzureCredentialOptions.ManagedIdentityClientId = tokenCredentialKey._clientId;
-#pragma warning disable CS0618 // Type or member is obsolete
- defaultAzureCredentialOptions.SharedTokenCacheUsername = tokenCredentialKey._clientId;
-#pragma warning restore CS0618 // Type or member is obsolete
- defaultAzureCredentialOptions.WorkloadIdentityClientId = tokenCredentialKey._clientId;
- }
-
- // SqlClient is a library and provides support to acquire access
- // token using 'DefaultAzureCredential' on user demand when they
- // specify 'Authentication = Active Directory Default' in
- // connection string.
- //
- // Default Azure Credential is instantiated by the calling
- // application when using "Active Directory Default"
- // authentication code to connect to Azure SQL instance.
- // SqlClient is a library, doesn't instantiate the credential
- // without running application instructions.
- //
- // Note that CodeQL suppression support can only detect
- // suppression comments that appear immediately above the
- // flagged statement, or appended to the end of the statement.
- // Multi-line justifications are not supported.
- //
- // https://eng.ms/docs/cloud-ai-platform/devdiv/one-engineering-system-1es/1es-docs/codeql/codeql-semmle#guidance-on-suppressions
- //
- // CodeQL [SM05137] See above for justification.
- DefaultAzureCredential cred = new(defaultAzureCredentialOptions);
-
- return new TokenCredentialData(cred, GetHash(secret));
- }
-
- TokenCredentialOptions tokenCredentialOptions = new() { AuthorityHost = new Uri(tokenCredentialKey._authority) };
-
- if (tokenCredentialKey._tokenCredentialType == typeof(ManagedIdentityCredential))
- {
- return new TokenCredentialData(new ManagedIdentityCredential(tokenCredentialKey._clientId, tokenCredentialOptions), GetHash(secret));
- }
- else if (tokenCredentialKey._tokenCredentialType == typeof(ClientSecretCredential))
- {
- return new TokenCredentialData(new ClientSecretCredential(tokenCredentialKey._audience, tokenCredentialKey._clientId, secret, tokenCredentialOptions), GetHash(secret));
- }
- else if (tokenCredentialKey._tokenCredentialType == typeof(WorkloadIdentityCredential))
- {
- // The WorkloadIdentityCredentialOptions object initialization populates its instance members
- // from the environment variables AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_FEDERATED_TOKEN_FILE,
- // and AZURE_ADDITIONALLY_ALLOWED_TENANTS. AZURE_CLIENT_ID may be overridden by the User Id.
- WorkloadIdentityCredentialOptions options = new() { AuthorityHost = new Uri(tokenCredentialKey._authority) };
-
- if (tokenCredentialKey._clientId is not null)
- {
- options.ClientId = tokenCredentialKey._clientId;
- }
-
- return new TokenCredentialData(new WorkloadIdentityCredential(options), GetHash(secret));
- }
-
- // This should never be reached, but if it is, throw an exception that will be noticed during development
- throw new ArgumentException(nameof(ActiveDirectoryAuthenticationProvider));
- }
-
- internal class PublicClientAppKey
- {
- public readonly string _authority;
- public readonly string _redirectUri;
- public readonly string _applicationClientId;
-#if NETFRAMEWORK
- public readonly Func _iWin32WindowFunc;
-#endif
-
- public PublicClientAppKey(string authority, string redirectUri, string applicationClientId
-#if NETFRAMEWORK
- , Func iWin32WindowFunc
-#endif
- )
- {
- _authority = authority;
- _redirectUri = redirectUri;
- _applicationClientId = applicationClientId;
-#if NETFRAMEWORK
- _iWin32WindowFunc = iWin32WindowFunc;
-#endif
- }
-
- public override bool Equals(object obj)
- {
- if (obj != null && obj is PublicClientAppKey pcaKey)
- {
- return (string.CompareOrdinal(_authority, pcaKey._authority) == 0
- && string.CompareOrdinal(_redirectUri, pcaKey._redirectUri) == 0
- && string.CompareOrdinal(_applicationClientId, pcaKey._applicationClientId) == 0
-#if NETFRAMEWORK
- && pcaKey._iWin32WindowFunc == _iWin32WindowFunc
-#endif
- );
- }
- return false;
- }
-
- public override int GetHashCode() => Tuple.Create(_authority, _redirectUri, _applicationClientId
-#if NETFRAMEWORK
- , _iWin32WindowFunc
-#endif
- ).GetHashCode();
- }
-
- internal class TokenCredentialData
- {
- public TokenCredential _tokenCredential;
- public byte[] _secretHash;
-
- public TokenCredentialData(TokenCredential tokenCredential, byte[] secretHash)
- {
- _tokenCredential = tokenCredential;
- _secretHash = secretHash;
- }
- }
-
- internal class TokenCredentialKey
- {
- public readonly Type _tokenCredentialType;
- public readonly string _authority;
- public readonly string _scope;
- public readonly string _audience;
- public readonly string _clientId;
-
- public TokenCredentialKey(Type tokenCredentialType, string authority, string scope, string audience, string clientId)
- {
- _tokenCredentialType = tokenCredentialType;
- _authority = authority;
- _scope = scope;
- _audience = audience;
- _clientId = clientId;
- }
-
- public override bool Equals(object obj)
- {
- if (obj != null && obj is TokenCredentialKey tcKey)
- {
- return string.CompareOrdinal(nameof(_tokenCredentialType), nameof(tcKey._tokenCredentialType)) == 0
- && string.CompareOrdinal(_authority, tcKey._authority) == 0
- && string.CompareOrdinal(_scope, tcKey._scope) == 0
- && string.CompareOrdinal(_audience, tcKey._audience) == 0
- && string.CompareOrdinal(_clientId, tcKey._clientId) == 0
- ;
- }
- return false;
- }
-
- public override int GetHashCode() => Tuple.Create(_tokenCredentialType, _authority, _scope, _audience, _clientId).GetHashCode();
- }
-
- }
-}
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationTimeoutRetryHelper.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationTimeoutRetryHelper.cs
index e972102260..721e31ca6f 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationTimeoutRetryHelper.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ActiveDirectoryAuthenticationTimeoutRetryHelper.cs
@@ -132,7 +132,8 @@ private static string GetTokenHash(SqlFedAuthToken token)
}
// Here we mimic how ADAL calculates hash for token. They use UTF8 instead of Unicode.
- var originalTokenString = SqlAuthenticationToken.AccessTokenStringFromBytes(token.accessToken);
+ var originalTokenString = Encoding.Unicode.GetString(token.AccessToken);
+
var bytesInUtf8 = Encoding.UTF8.GetBytes(originalTokenString);
using (var sha256 = SHA256.Create())
{
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Connection/SqlConnectionInternal.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Connection/SqlConnectionInternal.cs
index 5567dac6d5..efd54ae383 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Connection/SqlConnectionInternal.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Connection/SqlConnectionInternal.cs
@@ -8,7 +8,6 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
-using System.Net.Http.Headers;
using System.Security;
using System.Text;
using System.Threading;
@@ -19,7 +18,6 @@
using Microsoft.Data.ProviderBase;
using Microsoft.Data.SqlClient.Connection;
using Microsoft.Data.SqlClient.ConnectionPool;
-using Microsoft.Identity.Client;
using IsolationLevel = System.Data.IsolationLevel;
namespace Microsoft.Data.SqlClient.Connection
@@ -34,13 +32,6 @@ internal class SqlConnectionInternal : DbConnectionInternal, IDisposable
// @TODO: Can be private?
internal const int MaxNumberOfRedirectRoute = 10;
- ///
- /// Status code that indicates MSAL request should be retried.
- /// https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/retry-after#simple-retry-for-errors-with-http-error-codes-500-600
- ///
- // @TODO: Can be private?
- internal const int MsalHttpRetryStatusCode = 429;
-
///
/// The timespan defining the amount of time the authentication context needs to be valid
/// for at-least, to re-use the cached context, without making an attempt to refresh it. IF
@@ -132,20 +123,6 @@ internal class SqlConnectionInternal : DbConnectionInternal, IDisposable
#region Debug/Test Behavior Overrides
#if DEBUG
- ///
- /// This is a test hook to enable testing of the retry paths for MSAL get access token.
- ///
- ///
- /// Type type = typeof(SqlConnection).Assembly.GetType("Microsoft.Data.SqlClient.SQLInternalConnectionTds");
- /// FieldInfo field = type.GetField("_forceMsalRetry", BindingFlags.NonPublic | BindingFlags.Static);
- /// if (field != null)
- /// {
- /// field.SetValue(null, true);
- /// }
- ///
- /// @TODO: For unit tests, it should not be necessary to do this via reflection.
- internal static bool _forceMsalRetry = false;
-
///
/// This is a test hook to simulate a token expiring within the next 45 minutes.
///
@@ -619,7 +596,7 @@ internal bool Is2008OrNewer
internal override bool IsAccessTokenExpired
{
get => _federatedAuthenticationInfoRequested &&
- DateTime.FromFileTimeUtc(_fedAuthToken.expirationFileTime) < DateTime.UtcNow.AddSeconds(accessTokenExpirationBufferTime);
+ DateTime.FromFileTimeUtc(_fedAuthToken.ExpirationFileTime) < DateTime.UtcNow.AddSeconds(accessTokenExpirationBufferTime);
}
///
@@ -1806,8 +1783,8 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo)
// Construct the dbAuthenticationContextKey with information from FedAuthInfo and
// store for later use, when inserting in to the token cache.
_dbConnectionPoolAuthenticationContextKey = new DbConnectionPoolAuthenticationContextKey(
- fedAuthInfo.stsurl,
- fedAuthInfo.spn);
+ fedAuthInfo.StsUrl,
+ fedAuthInfo.Spn);
// Try to retrieve the authentication context from the pool, if one does exist for
// this key.
@@ -1928,14 +1905,12 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo)
// If the code flow is here, then we are re-using the context from the cache for
// this connection attempt and not generating a new access token on this thread.
- _fedAuthToken = new SqlFedAuthToken
- {
- accessToken = dbConnectionPoolAuthenticationContext.AccessToken,
- expirationFileTime = dbConnectionPoolAuthenticationContext.ExpirationTime.ToFileTime()
- };
+ _fedAuthToken = new(
+ dbConnectionPoolAuthenticationContext.AccessToken,
+ dbConnectionPoolAuthenticationContext.ExpirationTime.ToFileTime());
}
- Debug.Assert(_fedAuthToken?.accessToken != null,
+ Debug.Assert(_fedAuthToken?.AccessToken != null,
"_fedAuthToken and _fedAuthToken.accessToken cannot be null.");
_parser.SendFedAuthToken(_fedAuthToken);
@@ -2703,61 +2678,47 @@ private void FailoverPermissionDemand() =>
PoolGroupProviderInfo?.FailoverPermissionDemand();
#endif
+ #nullable enable
+
///
/// Get the Federated Authentication Token.
///
/// Information obtained from server as Federated Authentication Info.
private SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo)
{
- Debug.Assert(fedAuthInfo != null, "fedAuthInfo should not be null.");
-
- // Number of milliseconds to sleep for the initial back off.
- int sleepInterval = 100;
-
- // Number of attempts, for tracing purposes, if we underwent retries.
- int numberOfAttempts = 0;
+ // Number of milliseconds to sleep for the initial back off, if a
+ // retry period is not specified by the provider.
+ const int defaultRetryPeriod = 100;
- // Object that will be returned to the caller, containing all required data about the token.
- _fedAuthToken = new SqlFedAuthToken();
+ // Number of attempts we are willing to perform.
+ const int maxAttempts = 1;
// Username to use in error messages.
- string username = null;
-
- SqlAuthenticationProvider authProvider =
- SqlAuthenticationProvider.GetProvider(ConnectionOptions.Authentication);
+ string? username = null;
+ SqlAuthenticationProvider? authProvider = SqlAuthenticationProviderManager.GetProvider(ConnectionOptions.Authentication);
if (authProvider == null && _accessTokenCallback == null)
{
throw SQL.CannotFindAuthProvider(ConnectionOptions.Authentication.ToString());
}
- // Retry getting access token once if MsalException.error_code is unknown_error.
- // extra logic to deal with HTTP 429 (Retry after).
- // @TODO: Can we pick one or the other?
- // @TODO: Wait ... are we counting up but only looping while the number of attempts is <=1 ? Huh?
- // @TODO: Can we consider using a for loop here since there's a fixed number of times to loop
-
- #if NET
- while (numberOfAttempts <= 1)
- #else
- while (numberOfAttempts <= 1 && sleepInterval <= _timeout.MillisecondsRemaining)
- #endif
+ // We will perform retries if the provider indicates an error that
+ // is retryable.
+ for (int attempt = 0; attempt <= maxAttempts; ++attempt)
{
- numberOfAttempts++;
try
{
- var authParamsBuilder = new SqlAuthenticationParameters.Builder(
- authenticationMethod: ConnectionOptions.Authentication,
- resource: fedAuthInfo.spn,
- authority: fedAuthInfo.stsurl,
- serverName: ConnectionOptions.DataSource,
- databaseName: ConnectionOptions.InitialCatalog)
+ var authParamsBuilder = new SqlAuthenticationParametersBuilder(
+ authenticationMethod: ConnectionOptions.Authentication,
+ resource: fedAuthInfo.Spn,
+ authority: fedAuthInfo.StsUrl,
+ serverName: ConnectionOptions.DataSource,
+ databaseName: ConnectionOptions.InitialCatalog)
.WithConnectionId(_clientConnectionId)
- .WithConnectionTimeout(ConnectionOptions.ConnectTimeout);
+ .WithAuthenticationTimeout(ConnectionOptions.ConnectTimeout);
switch (ConnectionOptions.Authentication)
{
case SqlAuthenticationMethod.ActiveDirectoryIntegrated:
- #if NET
// In some scenarios for .NET Core, MSAL cannot detect the current user and needs it passed in
// for Integrated auth. Allow the user/application to pass it in to work around those scenarios.
if (!string.IsNullOrEmpty(ConnectionOptions.UserID))
@@ -2769,9 +2730,6 @@ private SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo)
{
username = TdsEnums.NTAUTHORITYANONYMOUSLOGON;
}
- #else
- username = TdsEnums.NTAUTHORITYANONYMOUSLOGON;
- #endif
if (_activeDirectoryAuthTimeoutRetryHelper.State == ActiveDirectoryAuthenticationTimeoutRetryState.Retrying)
{
@@ -2779,20 +2737,13 @@ private SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo)
}
else
{
- // We use Task.Run here in all places to execute task synchronously
- // in the same context. Fixes block-over-async deadlock possibilities
- // https://github.com/dotnet/SqlClient/issues/1209
- // @TODO: Verify that the wrapping/unwrapping is necessary.
- _fedAuthToken = Task.Run(async () =>
- await authProvider.AcquireTokenAsync(authParamsBuilder))
- .GetAwaiter()
- .GetResult()
- .ToSqlFedAuthToken();
+ // We use Task.Run here in all places to execute task synchronously in the same context.
+ // Fixes block-over-async deadlock possibilities https://github.com/dotnet/SqlClient/issues/1209
+ _fedAuthToken = new(Task.Run(async () => await authProvider!.AcquireTokenAsync(authParamsBuilder)).GetAwaiter().GetResult());
+
_activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken;
}
-
break;
-
case SqlAuthenticationMethod.ActiveDirectoryInteractive:
case SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow:
case SqlAuthenticationMethod.ActiveDirectoryManagedIdentity:
@@ -2806,16 +2757,13 @@ await authProvider.AcquireTokenAsync(authParamsBuilder))
else
{
authParamsBuilder.WithUserId(ConnectionOptions.UserID);
- _fedAuthToken = Task.Run(async () => await authProvider.AcquireTokenAsync(authParamsBuilder)).GetAwaiter().GetResult().ToSqlFedAuthToken();
+ _fedAuthToken = new(Task.Run(async () => await authProvider!.AcquireTokenAsync(authParamsBuilder)).GetAwaiter().GetResult());
_activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken;
}
-
break;
-
#pragma warning disable 0618 // Type or member is obsolete
case SqlAuthenticationMethod.ActiveDirectoryPassword:
#pragma warning restore 0618 // Type or member is obsolete
-
case SqlAuthenticationMethod.ActiveDirectoryServicePrincipal:
if (_activeDirectoryAuthTimeoutRetryHelper.State == ActiveDirectoryAuthenticationTimeoutRetryState.Retrying)
{
@@ -2823,32 +2771,21 @@ await authProvider.AcquireTokenAsync(authParamsBuilder))
}
else
{
- // @TODO: _fedAuthToken assignment is identical in both cases - move outside
if (_credential != null)
{
username = _credential.UserId;
authParamsBuilder.WithUserId(username).WithPassword(_credential.Password);
- _fedAuthToken = Task.Run(async () =>
- await authProvider.AcquireTokenAsync(authParamsBuilder))
- .GetAwaiter()
- .GetResult()
- .ToSqlFedAuthToken();
+ _fedAuthToken = new(Task.Run(async () => await authProvider!.AcquireTokenAsync(authParamsBuilder)).GetAwaiter().GetResult());
}
else
{
username = ConnectionOptions.UserID;
authParamsBuilder.WithUserId(username).WithPassword(ConnectionOptions.Password);
- _fedAuthToken = Task.Run(async () =>
- await authProvider.AcquireTokenAsync(authParamsBuilder))
- .GetAwaiter()
- .GetResult()
- .ToSqlFedAuthToken();
+ _fedAuthToken = new(Task.Run(async () => await authProvider!.AcquireTokenAsync(authParamsBuilder)).GetAwaiter().GetResult());
}
_activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken;
}
-
break;
-
default:
if (_accessTokenCallback == null)
{
@@ -2871,150 +2808,87 @@ await authProvider.AcquireTokenAsync(authParamsBuilder))
authParamsBuilder.WithUserId(ConnectionOptions.UserID);
authParamsBuilder.WithPassword(ConnectionOptions.Password);
}
-
SqlAuthenticationParameters parameters = authParamsBuilder;
- using CancellationTokenSource cts = new();
-
- // Use Connection timeout value to cancel token acquire request
- // after certain period of time.(int)
- if (_timeout.MillisecondsRemaining < int.MaxValue)
+ CancellationTokenSource cts = new();
+ // Use Connection timeout value to cancel token acquire request after certain period of time.(int)
+ if (_timeout.MillisecondsRemaining < Int32.MaxValue)
{
cts.CancelAfter((int)_timeout.MillisecondsRemaining);
}
-
- _fedAuthToken = Task.Run(async () =>
- await _accessTokenCallback(parameters, cts.Token))
- .GetAwaiter()
- .GetResult()
- .ToSqlFedAuthToken();
+ _fedAuthToken = new(Task.Run(async () => await _accessTokenCallback(parameters, cts.Token)).GetAwaiter().GetResult());
_activeDirectoryAuthTimeoutRetryHelper.CachedToken = _fedAuthToken;
}
break;
}
- Debug.Assert(_fedAuthToken.accessToken != null, "AccessToken should not be null.");
-
- #if DEBUG
- if (_forceMsalRetry)
- {
- // 3399614468 is 0xCAA20004L just for testing.
- throw new MsalServiceException(MsalError.UnknownError, "Force retry in GetFedAuthToken");
- }
- #endif
+ Debug.Assert(_fedAuthToken.AccessToken != null, "AccessToken should not be null.");
// Break out of the retry loop in successful case.
break;
}
- catch (MsalServiceException serviceException)
+ catch (SqlAuthenticationProviderException ex)
{
- // Deal with Msal service exceptions first, retry if 429 received.
- if (serviceException.StatusCode == MsalHttpRetryStatusCode)
+ // Is the error fatal or retryable?
+ if (!ex.ShouldRetry)
{
- RetryConditionHeaderValue retryAfter = serviceException.Headers.RetryAfter;
- if (retryAfter.Delta.HasValue)
- {
- sleepInterval = retryAfter.Delta.Value.Milliseconds;
- }
- else if (retryAfter.Date.HasValue)
- {
- sleepInterval = Convert.ToInt32(retryAfter.Date.Value.Offset.TotalMilliseconds);
- }
+ // It's fatal, so translate into a SqlException.
+ throw ADP.CreateSqlException(ex, ConnectionOptions, this, username);
+ }
- // if there's enough time to retry before timeout, then retry, otherwise
- // break out the retry loop.
- if (sleepInterval < _timeout.MillisecondsRemaining)
- {
- Thread.Sleep(sleepInterval);
- }
- else
- {
- SqlClientEventSource.Log.TryTraceEvent(
- $"SqlInternalConnectionTds.GetFedAuthToken.MsalServiceException | ERR | " +
- $"Timeout: {serviceException.ErrorCode}");
+ // We should retry.
- throw SQL.ActiveDirectoryTokenRetrievingTimeout(
- Enum.GetName(typeof(SqlAuthenticationMethod), ConnectionOptions.Authentication),
- serviceException.ErrorCode,
- serviceException);
- }
- }
- else
+ // Could we retry if we wanted to?
+ if (_timeout.IsExpired || _timeout.MillisecondsRemaining <= 0)
{
- SqlClientEventSource.Log.TryTraceEvent(
- $"SqlInternalConnectionTds.GetFedAuthToken.MsalServiceException | ERR | " +
- $"{serviceException.ErrorCode}");
-
- throw ADP.CreateSqlException(serviceException, ConnectionOptions, this, username);
+ // No, so we throw.
+ SqlClientEventSource.Log.TryTraceEvent(" Attempt: {0}, Timeout: {1}", attempt, ex.FailureCode);
+ throw SQL.ActiveDirectoryTokenRetrievingTimeout(Enum.GetName(typeof(SqlAuthenticationMethod), ConnectionOptions.Authentication), ex.FailureCode, ex);
}
- }
- catch (MsalException msalException)
- {
- // Deal with normal MsalExceptions.
- if (MsalError.UnknownError != msalException.ErrorCode ||
- _timeout.IsExpired ||
- _timeout.MillisecondsRemaining <= sleepInterval)
- {
- SqlClientEventSource.Log.TryTraceEvent(
- $"SqlInternalConnectionTds.GetFedAuthToken.MSALException | ERR | " +
- $"{msalException.ErrorCode}");
- throw ADP.CreateSqlException(msalException, ConnectionOptions, this, username);
- }
+ // We use a doubling backoff if the provider didn't provide
+ // a retry period.
+ int retryPeriod =
+ ex.RetryPeriod > 0
+ ? ex.RetryPeriod
+ : defaultRetryPeriod * (2 ^ attempt);
- SqlClientEventSource.Log.TryAdvancedTraceEvent(
- $"SqlInternalConnectionTds.GetFedAuthToken | ADV | " +
- $"Object ID: {ObjectID}, " +
- $"sleeping {sleepInterval}ms");
- SqlClientEventSource.Log.TryAdvancedTraceEvent(
- $"SqlInternalConnectionTds.GetFedAuthToken | ADV | " +
- $"Object ID: {ObjectID}, " +
- $"remaining {_timeout.MillisecondsRemaining}ms");
+ SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, Attempt: {1}, sleeping {2}[Milliseconds]", ObjectID, attempt, retryPeriod);
+ SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, Attempt: {1}, remaining {2}[Milliseconds]", ObjectID, attempt, _timeout.MillisecondsRemaining);
- Thread.Sleep(sleepInterval);
+ // Sleep for the desired period.
+ Thread.Sleep(retryPeriod);
- sleepInterval *= 2;
- }
- // All other exceptions from MSAL/Azure Identity APIs
- catch (Exception e)
- {
- SqlError error = new(
- infoNumber: 0,
- errorState: 0x00,
- errorClass: TdsEnums.FATAL_ERROR_CLASS,
- server: ConnectionOptions.DataSource,
- errorMessage: e.Message,
- procedure: ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName,
- lineNumber: 0);
-
- throw SqlException.CreateException(
- error,
- serverVersion: string.Empty,
- internalConnection: this,
- innerException: e);
+ // Fall through to retry...
}
}
- Debug.Assert(_fedAuthToken != null, "fedAuthToken should not be null.");
- Debug.Assert(_fedAuthToken.accessToken?.Length > 0,
- "fedAuthToken.accessToken should not be null or empty.");
+ // Nullable context has exposed that _fedAuthToken may be null here,
+ // which the existing code didn't handle.
+ if (_fedAuthToken is null)
+ {
+ // We failed to acquire a token, so use a default one.
+ //
+ // TODO: The old code actually allowed the AccessToken byte
+ // array to be null, and then had Debug.Assert()s to verify it
+ // wasn't null. We never test in debug, so those were never
+ // firing, and we were happily using a _fedAuthToken with a null
+ // AccessToken. Now that SqlFedAuthToken doesn't allow a null
+ // AccessToken, we just create an empty one instead.
+ _fedAuthToken = new SqlFedAuthToken([], 0);
+ }
// Store the newly generated token in _newDbConnectionPoolAuthenticationContext, only if using pooling.
if (_dbConnectionPool != null)
{
- DateTime expirationTime = DateTime.FromFileTimeUtc(_fedAuthToken.expirationFileTime);
- _newDbConnectionPoolAuthenticationContext = new DbConnectionPoolAuthenticationContext(
- _fedAuthToken.accessToken,
- expirationTime);
+ DateTime expirationTime = DateTime.FromFileTimeUtc(_fedAuthToken.ExpirationFileTime);
+ _newDbConnectionPoolAuthenticationContext = new DbConnectionPoolAuthenticationContext(_fedAuthToken.AccessToken, expirationTime);
}
-
- SqlClientEventSource.Log.TryTraceEvent(
- $"SqlInternalConnectionTds.GetFedAuthToken | " +
- $"Object ID {ObjectID}, " +
- $"Finished generating federated authentication token.");
-
+ SqlClientEventSource.Log.TryTraceEvent(" {0}, Finished generating federated authentication token.", ObjectID);
return _fedAuthToken;
}
+ #nullable disable
+
private void Login(
ServerInfo server,
TimeoutTimer timeout,
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAuthenticationParameters.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAuthenticationParameters.cs
deleted file mode 100644
index 9c74b937b8..0000000000
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAuthenticationParameters.cs
+++ /dev/null
@@ -1,162 +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 System;
-using System.Runtime.InteropServices;
-using System.Security;
-using Microsoft.Data.Common;
-
-namespace Microsoft.Data.SqlClient
-{
-
- ///
- public class SqlAuthenticationParameters
- {
- ///
- public SqlAuthenticationMethod AuthenticationMethod { get; }
-
- ///
- public string Resource { get; }
-
- ///
- public string Authority { get; }
-
- ///
- public string UserId { get; }
-
- ///
- public string Password { get; }
-
- ///
- public Guid ConnectionId { get; }
-
- ///
- public string ServerName { get; }
-
- ///
- public string DatabaseName { get; }
-
- ///
- public int ConnectionTimeout { get; } = ADP.DefaultConnectionTimeout;
-
- ///
- protected SqlAuthenticationParameters(
- SqlAuthenticationMethod authenticationMethod,
- string serverName,
- string databaseName,
- string resource,
- string authority,
- string userId,
- string password,
- Guid connectionId,
- int connectionTimeout)
- {
- AuthenticationMethod = authenticationMethod;
- ServerName = serverName;
- DatabaseName = databaseName;
- Resource = resource;
- Authority = authority;
- UserId = userId;
- Password = password;
- ConnectionId = connectionId;
- ConnectionTimeout = connectionTimeout;
- }
-
- ///
- /// AD authentication parameter builder.
- ///
- internal class Builder
- {
- private readonly SqlAuthenticationMethod _authenticationMethod;
- private readonly string _serverName;
- private readonly string _databaseName;
- private readonly string _resource;
- private readonly string _authority;
- private string _userId;
- private string _password;
- private Guid _connectionId = Guid.NewGuid();
- private int _connectionTimeout = ADP.DefaultConnectionTimeout;
-
- ///
- /// Implicitly converts to .
- ///
- public static implicit operator SqlAuthenticationParameters(Builder builder)
- {
- return new SqlAuthenticationParameters(
- authenticationMethod: builder._authenticationMethod,
- serverName: builder._serverName,
- databaseName: builder._databaseName,
- resource: builder._resource,
- authority: builder._authority,
- userId: builder._userId,
- password: builder._password,
- connectionId: builder._connectionId,
- connectionTimeout: builder._connectionTimeout);
- }
-
- ///
- /// Set user id.
- ///
- public Builder WithUserId(string userId)
- {
- _userId = userId;
- return this;
- }
-
- ///
- /// Set password.
- ///
- public Builder WithPassword(string password)
- {
- _password = password;
- return this;
- }
-
- ///
- /// Set password.
- ///
- public Builder WithPassword(SecureString password)
- {
- IntPtr valuePtr = IntPtr.Zero;
- try
- {
- valuePtr = Marshal.SecureStringToGlobalAllocUnicode(password);
- _password = Marshal.PtrToStringUni(valuePtr);
- }
- finally
- {
- Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
- }
- return this;
- }
-
- ///
- /// Set a specific connection id instead of using a random one.
- ///
- public Builder WithConnectionId(Guid connectionId)
- {
- _connectionId = connectionId;
- return this;
- }
-
- ///
- /// Set connection timeout.
- ///
- public Builder WithConnectionTimeout(int timeout)
- {
- _connectionTimeout = timeout;
- return this;
- }
-
- internal Builder(SqlAuthenticationMethod authenticationMethod, string resource, string authority, string serverName, string databaseName)
- {
- _authenticationMethod = authenticationMethod;
- _serverName = serverName;
- _databaseName = databaseName;
- _resource = resource;
- _authority = authority;
- }
- }
- }
-}
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAuthenticationParametersBuilder.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAuthenticationParametersBuilder.cs
new file mode 100644
index 0000000000..a9863ee2ac
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAuthenticationParametersBuilder.cs
@@ -0,0 +1,104 @@
+// 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.Runtime.InteropServices;
+using System.Security;
+using Microsoft.Data.Common;
+
+namespace Microsoft.Data.SqlClient
+{
+ internal sealed class SqlAuthenticationParametersBuilder
+ {
+ private readonly SqlAuthenticationMethod _authenticationMethod;
+ private readonly string _serverName;
+ private readonly string _databaseName;
+ private readonly string _resource;
+ private readonly string _authority;
+ private string _userId;
+ private string _password;
+ private Guid _connectionId = Guid.NewGuid();
+ private int _authenticationTimeout = ADP.DefaultConnectionTimeout;
+
+ ///
+ /// Implicitly converts to .
+ ///
+ public static implicit operator SqlAuthenticationParameters(SqlAuthenticationParametersBuilder builder)
+ {
+ return new SqlAuthenticationParameters(
+ authenticationMethod: builder._authenticationMethod,
+ serverName: builder._serverName,
+ databaseName: builder._databaseName,
+ resource: builder._resource,
+ authority: builder._authority,
+ userId: builder._userId,
+ password: builder._password,
+ connectionId: builder._connectionId,
+ connectionTimeout: builder._authenticationTimeout);
+ }
+
+ ///
+ /// Set user id.
+ ///
+ public SqlAuthenticationParametersBuilder WithUserId(string userId)
+ {
+ _userId = userId;
+ return this;
+ }
+
+ ///
+ /// Set password.
+ ///
+ public SqlAuthenticationParametersBuilder WithPassword(string password)
+ {
+ _password = password;
+ return this;
+ }
+
+ ///
+ /// Set password.
+ ///
+ public SqlAuthenticationParametersBuilder WithPassword(SecureString password)
+ {
+ IntPtr valuePtr = IntPtr.Zero;
+ try
+ {
+ valuePtr = Marshal.SecureStringToGlobalAllocUnicode(password);
+ _password = Marshal.PtrToStringUni(valuePtr);
+ }
+ finally
+ {
+ Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
+ }
+ return this;
+ }
+
+ ///
+ /// Set a specific connection id instead of using a random one.
+ ///
+ public SqlAuthenticationParametersBuilder WithConnectionId(Guid connectionId)
+ {
+ _connectionId = connectionId;
+ return this;
+ }
+
+ ///
+ /// Set authentication timeout.
+ ///
+ public SqlAuthenticationParametersBuilder WithAuthenticationTimeout(int timeout)
+ {
+ _authenticationTimeout = timeout;
+ return this;
+ }
+
+ internal SqlAuthenticationParametersBuilder(SqlAuthenticationMethod authenticationMethod, string resource, string authority, string serverName, string databaseName)
+ {
+ _authenticationMethod = authenticationMethod;
+ _serverName = serverName;
+ _databaseName = databaseName;
+ _resource = resource;
+ _authority = authority;
+ }
+ }
+}
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAuthenticationProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAuthenticationProvider.cs
deleted file mode 100644
index 25e1cf006e..0000000000
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAuthenticationProvider.cs
+++ /dev/null
@@ -1,37 +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 System.Threading.Tasks;
-
-namespace Microsoft.Data.SqlClient
-{
-
- ///
- public abstract class SqlAuthenticationProvider
- {
- ///
- public static SqlAuthenticationProvider GetProvider(SqlAuthenticationMethod authenticationMethod)
- {
- return SqlAuthenticationProviderManager.Instance.GetProvider(authenticationMethod);
- }
-
- ///
- public static bool SetProvider(SqlAuthenticationMethod authenticationMethod, SqlAuthenticationProvider provider)
- {
- return SqlAuthenticationProviderManager.Instance.SetProvider(authenticationMethod, provider);
- }
-
- ///
- public virtual void BeforeLoad(SqlAuthenticationMethod authenticationMethod) { }
-
- ///
- public virtual void BeforeUnload(SqlAuthenticationMethod authenticationMethod) { }
-
- ///
- public abstract bool IsSupported(SqlAuthenticationMethod authenticationMethod);
-
- ///
- public abstract Task AcquireTokenAsync(SqlAuthenticationParameters parameters);
- }
-}
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs
index 447ea0e9c5..06d7423028 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAuthenticationProviderManager.cs
@@ -6,12 +6,13 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Configuration;
+using System.IO;
+using System.Reflection;
+
+#nullable enable
namespace Microsoft.Data.SqlClient
{
- ///
- /// Authentication provider manager.
- ///
internal sealed class SqlAuthenticationProviderManager
{
[Obsolete("ActiveDirectoryPassword is deprecated, use a more secure authentication method. See https://aka.ms/SqlClientEntraIDAuthentication for more details.")]
@@ -27,7 +28,7 @@ internal sealed class SqlAuthenticationProviderManager
static SqlAuthenticationProviderManager()
{
- SqlAuthenticationProviderConfigurationSection configurationSection = null;
+ SqlAuthenticationProviderConfigurationSection? configurationSection = null;
try
{
@@ -46,49 +47,125 @@ static SqlAuthenticationProviderManager()
}
Instance = new SqlAuthenticationProviderManager(configurationSection);
- SetDefaultAuthProviders(Instance);
- }
- ///
- /// Sets default supported Active Directory Authentication providers by the driver
- /// on the SqlAuthenticationProviderManager instance.
- ///
- private static void SetDefaultAuthProviders(SqlAuthenticationProviderManager instance)
- {
- if (instance != null)
+ // If our Azure extensions package is present, use its
+ // authentication provider as our default.
+ const string assemblyName = "Microsoft.Data.SqlClient.Extensions.Azure";
+
+ try
{
- var activeDirectoryAuthProvider = new ActiveDirectoryAuthenticationProvider(instance._applicationClientId);
- instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryIntegrated, activeDirectoryAuthProvider);
+ // Try to load our Azure extension.
+ var assembly = Assembly.Load(assemblyName);
+
+ if (assembly is null)
+ {
+ SqlClientEventSource.Log.TryTraceEvent(
+ nameof(SqlAuthenticationProviderManager) +
+ $": Azure extension assembly={assemblyName} not found; " +
+ "no default Active Directory provider installed");
+ return;
+ }
+
+ // TODO(ADO-39845): Verify the assembly is signed by us?
+
+ 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 authentication provider class.
+ const string className = "Microsoft.Data.SqlClient.ActiveDirectoryAuthenticationProvider";
+ var type = assembly.GetType(className);
+
+ if (type is null)
+ {
+ SqlClientEventSource.Log.TryTraceEvent(
+ nameof(SqlAuthenticationProviderManager) +
+ $": Azure extension does not contain class={className}; " +
+ "no default Active Directory provider installed");
+
+ return;
+ }
+
+ // Try to instantiate it.
+ var instance = Activator.CreateInstance(
+ type,
+ [Instance._applicationClientId])
+ as SqlAuthenticationProvider;
+
+ if (instance is null)
+ {
+ SqlClientEventSource.Log.TryTraceEvent(
+ nameof(SqlAuthenticationProviderManager) +
+ $": Failed to instantiate Azure extension class={className}; " +
+ "no default Active Directory provider installed");
+
+ return;
+ }
+
+ // We successfully instantiated the provider, so set it as the
+ // default for all Active Directory authentication methods.
+ //
+ // Note that SetProvider() will refuse to clobber an application
+ // specified provider, so these defaults will only be applied
+ // for methods that do not already have a provider.
+ SetProvider(SqlAuthenticationMethod.ActiveDirectoryIntegrated, instance);
#pragma warning disable 0618 // Type or member is obsolete
- instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, activeDirectoryAuthProvider);
+ SetProvider(SqlAuthenticationMethod.ActiveDirectoryPassword, instance);
#pragma warning restore 0618 // Type or member is obsolete
- instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryInteractive, activeDirectoryAuthProvider);
- instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryServicePrincipal, activeDirectoryAuthProvider);
- instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow, activeDirectoryAuthProvider);
- instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity, activeDirectoryAuthProvider);
- instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryMSI, activeDirectoryAuthProvider);
- instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDefault, activeDirectoryAuthProvider);
- instance.SetProvider(SqlAuthenticationMethod.ActiveDirectoryWorkloadIdentity, activeDirectoryAuthProvider);
+ SetProvider(SqlAuthenticationMethod.ActiveDirectoryInteractive, instance);
+ SetProvider(SqlAuthenticationMethod.ActiveDirectoryServicePrincipal, instance);
+ SetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow, instance);
+ SetProvider(SqlAuthenticationMethod.ActiveDirectoryManagedIdentity, instance);
+ SetProvider(SqlAuthenticationMethod.ActiveDirectoryMSI, instance);
+ SetProvider(SqlAuthenticationMethod.ActiveDirectoryDefault, instance);
+ SetProvider(SqlAuthenticationMethod.ActiveDirectoryWorkloadIdentity, instance);
+
+ SqlClientEventSource.Log.TryTraceEvent(
+ nameof(SqlAuthenticationProviderManager) +
+ $": Azure extension class={className} installed as " +
+ "provider for all Active Directory authentication methods");
}
+ // All of these exceptions mean we couldn't find or instantiate the
+ // Azure extension's authentication provider, in which case we
+ // simply have no default and the app must provide one if they
+ // attempt to use Active Directory authentication.
+ catch (Exception ex)
+ when (ex is ArgumentNullException ||
+ ex is ArgumentException ||
+ ex is BadImageFormatException ||
+ ex is FileLoadException ||
+ ex is FileNotFoundException ||
+ ex is MemberAccessException ||
+ ex is MethodAccessException ||
+ ex is MissingMethodException ||
+ ex is NotSupportedException ||
+ ex is TargetInvocationException ||
+ ex is TypeLoadException)
+ {
+ SqlClientEventSource.Log.TryTraceEvent(
+ nameof(SqlAuthenticationProviderManager) +
+ $": Azure extension assembly={assemblyName} not found or " +
+ "not usable; no default provider installed; " +
+ $"{ex.GetType().Name}: {ex.Message}");
+ }
+ // Any other exceptions are fatal.
}
- public static readonly SqlAuthenticationProviderManager Instance;
+ private static readonly SqlAuthenticationProviderManager Instance;
- private readonly SqlAuthenticationInitializer _initializer;
- private readonly IReadOnlyCollection _authenticationsWithAppSpecifiedProvider;
- private readonly ConcurrentDictionary _providers;
+ private readonly HashSet _authenticationsWithAppSpecifiedProvider = new();
+ private readonly ConcurrentDictionary _providers = new();
private readonly SqlClientLogger _sqlAuthLogger = new SqlClientLogger();
- private readonly string _applicationClientId = ActiveDirectoryAuthentication.AdoClientId;
+ private readonly string? _applicationClientId = null;
///
/// Constructor.
///
- public SqlAuthenticationProviderManager(SqlAuthenticationProviderConfigurationSection configSection = null)
+ private SqlAuthenticationProviderManager(SqlAuthenticationProviderConfigurationSection? configSection)
{
var methodName = "Ctor";
- _providers = new ConcurrentDictionary();
- var authenticationsWithAppSpecifiedProvider = new HashSet();
- _authenticationsWithAppSpecifiedProvider = authenticationsWithAppSpecifiedProvider;
if (configSection == null)
{
@@ -112,8 +189,14 @@ public SqlAuthenticationProviderManager(SqlAuthenticationProviderConfigurationSe
try
{
var initializerType = Type.GetType(configSection.InitializerType, true);
- _initializer = (SqlAuthenticationInitializer)Activator.CreateInstance(initializerType);
- _initializer.Initialize();
+ if (initializerType is not null)
+ {
+ var initializer = (SqlAuthenticationInitializer?)Activator.CreateInstance(initializerType);
+ if (initializer is not null)
+ {
+ initializer.Initialize();
+ }
+ }
}
catch (Exception e)
{
@@ -132,23 +215,31 @@ public SqlAuthenticationProviderManager(SqlAuthenticationProviderConfigurationSe
foreach (ProviderSettings providerSettings in configSection.Providers)
{
SqlAuthenticationMethod authentication = AuthenticationEnumFromString(providerSettings.Name);
- SqlAuthenticationProvider provider;
+ SqlAuthenticationProvider? provider;
try
{
var providerType = Type.GetType(providerSettings.Type, true);
- provider = (SqlAuthenticationProvider)Activator.CreateInstance(providerType);
+ if (providerType is null)
+ {
+ continue;
+ }
+ provider = (SqlAuthenticationProvider?)Activator.CreateInstance(providerType);
}
catch (Exception e)
{
throw SQL.CannotCreateAuthProvider(authentication.ToString(), providerSettings.Type, e);
}
+ if (provider is null)
+ {
+ continue;
+ }
if (!provider.IsSupported(authentication))
{
throw SQL.UnsupportedAuthenticationByProvider(authentication.ToString(), providerSettings.Type);
}
_providers[authentication] = provider;
- authenticationsWithAppSpecifiedProvider.Add(authentication);
+ _authenticationsWithAppSpecifiedProvider.Add(authentication);
_sqlAuthLogger.LogInfo(nameof(SqlAuthenticationProviderManager), methodName, string.Format("Added user-defined auth provider: {0} for authentication {1}.", providerSettings?.Type, authentication));
}
}
@@ -158,54 +249,48 @@ public SqlAuthenticationProviderManager(SqlAuthenticationProviderConfigurationSe
}
}
- ///
- /// Get an authentication provider by method.
- ///
- /// Authentication method.
- /// Authentication provider or null if not found.
- public SqlAuthenticationProvider GetProvider(SqlAuthenticationMethod authenticationMethod)
+ internal static SqlAuthenticationProvider? GetProvider(SqlAuthenticationMethod authenticationMethod)
{
- SqlAuthenticationProvider value;
- return _providers.TryGetValue(authenticationMethod, out value) ? value : null;
+ SqlAuthenticationProvider? value;
+ return Instance._providers.TryGetValue(authenticationMethod, out value) ? value : null;
}
- ///
- /// Set an authentication provider by method.
- ///
- /// Authentication method.
- /// Authentication provider.
- /// True if succeeded, false otherwise, e.g., the existing provider disallows overriding.
- public bool SetProvider(SqlAuthenticationMethod authenticationMethod, SqlAuthenticationProvider provider)
+ internal static bool SetProvider(SqlAuthenticationMethod authenticationMethod, SqlAuthenticationProvider provider)
{
if (!provider.IsSupported(authenticationMethod))
{
throw SQL.UnsupportedAuthenticationByProvider(authenticationMethod.ToString(), provider.GetType().Name);
}
var methodName = "SetProvider";
- if (_authenticationsWithAppSpecifiedProvider.Count > 0)
+ if (Instance._authenticationsWithAppSpecifiedProvider.Count > 0)
{
- foreach (SqlAuthenticationMethod candidateMethod in _authenticationsWithAppSpecifiedProvider)
+ foreach (SqlAuthenticationMethod candidateMethod in Instance._authenticationsWithAppSpecifiedProvider)
{
if (candidateMethod == authenticationMethod)
{
- _sqlAuthLogger.LogError(nameof(SqlAuthenticationProviderManager), methodName, $"Failed to add provider {GetProviderType(provider)} because a user-defined provider with type {GetProviderType(_providers[authenticationMethod])} already existed for authentication {authenticationMethod}.");
- return false; // return here to avoid replacing user-defined provider
+ Instance._sqlAuthLogger.LogError(nameof(SqlAuthenticationProviderManager), methodName, $"Failed to add provider {GetProviderType(provider)} because a user-defined provider with type {GetProviderType(Instance._providers[authenticationMethod])} already existed for authentication {authenticationMethod}.");
+
+ // The app has already specified a Provider for this
+ // authentication method, so we won't override it.
+ return false;
}
}
}
- _providers.AddOrUpdate(authenticationMethod, provider, (key, oldProvider) =>
- {
- if (oldProvider != null)
- {
- oldProvider.BeforeUnload(authenticationMethod);
- }
- if (provider != null)
+ Instance._providers.AddOrUpdate(
+ authenticationMethod,
+ provider,
+ (SqlAuthenticationMethod key, SqlAuthenticationProvider oldProvider) =>
{
+ if (oldProvider != null)
+ {
+ oldProvider.BeforeUnload(authenticationMethod);
+ }
+
provider.BeforeLoad(authenticationMethod);
- }
- _sqlAuthLogger.LogInfo(nameof(SqlAuthenticationProviderManager), methodName, $"Added auth provider {GetProviderType(provider)}, overriding existed provider {GetProviderType(oldProvider)} for authentication {authenticationMethod}.");
- return provider;
- });
+
+ Instance._sqlAuthLogger.LogInfo(nameof(SqlAuthenticationProviderManager), methodName, $"Added auth provider {GetProviderType(provider)}, overriding existed provider {GetProviderType(oldProvider)} for authentication {authenticationMethod}.");
+ return provider;
+ });
return true;
}
@@ -216,7 +301,7 @@ public bool SetProvider(SqlAuthenticationMethod authenticationMethod, SqlAuthent
///
///
///
- private static T FetchConfigurationSection(string name)
+ private static T? FetchConfigurationSection(string name) where T : class
{
Type t = typeof(T);
@@ -265,14 +350,13 @@ private static SqlAuthenticationMethod AuthenticationEnumFromString(string authe
}
}
- private static string GetProviderType(SqlAuthenticationProvider provider)
+ private static string GetProviderType(SqlAuthenticationProvider? provider)
{
- if (provider == null)
+ if (provider is null)
{
return "null";
}
-
- return provider.GetType().FullName;
+ return provider.GetType().FullName ?? "unknown";
}
}
@@ -293,13 +377,13 @@ internal class SqlAuthenticationProviderConfigurationSection : ConfigurationSect
/// User-defined initializer.
///
[ConfigurationProperty("initializerType")]
- public string InitializerType => this["initializerType"] as string;
+ public string InitializerType => this["initializerType"] as string ?? string.Empty;
///
/// Application Client Id
///
[ConfigurationProperty("applicationClientId", IsRequired = false)]
- public string ApplicationClientId => this["applicationClientId"] as string;
+ public string ApplicationClientId => this["applicationClientId"] as string ?? string.Empty;
}
///
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAuthenticationToken.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAuthenticationToken.cs
deleted file mode 100644
index c405cac2cb..0000000000
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlAuthenticationToken.cs
+++ /dev/null
@@ -1,67 +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 System;
-using System.Text;
-
-namespace Microsoft.Data.SqlClient
-{
- ///
- public class SqlAuthenticationToken
- {
- ///
- public DateTimeOffset ExpiresOn { get; }
-
- ///
- public string AccessToken { get; }
-
- ///
- public SqlAuthenticationToken(string accessToken, DateTimeOffset expiresOn)
- {
- if (string.IsNullOrEmpty(accessToken))
- {
- throw SQL.ParameterCannotBeEmpty("AccessToken");
- }
-
- AccessToken = accessToken;
- ExpiresOn = expiresOn;
- }
-
- ///
- /// Constructor.
- ///
- internal SqlAuthenticationToken(byte[] accessToken, DateTimeOffset expiresOn)
- : this(AccessTokenStringFromBytes(accessToken), expiresOn) { }
-
- ///
- /// Convert to driver's internal token class.
- ///
- internal SqlFedAuthToken ToSqlFedAuthToken()
- {
- var tokenBytes = AccessTokenBytesFromString(AccessToken);
- return new SqlFedAuthToken
- {
- accessToken = tokenBytes,
- dataLen = (uint)tokenBytes.Length,
- expirationFileTime = ExpiresOn.ToFileTime()
- };
- }
-
- ///
- /// Convert token bytes to string.
- ///
- internal static string AccessTokenStringFromBytes(byte[] bytes)
- {
- return Encoding.Unicode.GetString(bytes);
- }
-
- ///
- /// Convert token string to bytes.
- ///
- internal static byte[] AccessTokenBytesFromString(string token)
- {
- return Encoding.Unicode.GetBytes(token);
- }
- }
-}
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlErrorCollection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlErrorCollection.cs
index 4684747627..fe20d0d2e5 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlErrorCollection.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlErrorCollection.cs
@@ -45,6 +45,11 @@ internal SqlErrorCollection() { }
///
public IEnumerator GetEnumerator() => _errors.GetEnumerator();
- internal void Add(SqlError error) => _errors.Add(error);
+ // Append the error to our list, and return ourselves for chaining.
+ internal SqlErrorCollection Add(SqlError error)
+ {
+ _errors.Add(error);
+ return this;
+ }
}
}
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlException.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlException.cs
index b392a94521..17fccca6c5 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlException.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlException.cs
@@ -254,8 +254,6 @@ internal static SqlException CreateException(
Exception innerException = null,
SqlBatchCommand batchCommand = null)
{
- Debug.Assert(errorCollection != null && errorCollection.Count > 0, "no errorCollection?");
-
StringBuilder message = new();
for (int i = 0; i < errorCollection.Count; i++)
{
@@ -266,7 +264,11 @@ internal static SqlException CreateException(
message.Append(errorCollection[i].Message);
}
- if (innerException == null && errorCollection[0].Win32ErrorCode != 0 && errorCollection[0].Win32ErrorCode != -1)
+ if (innerException is null &&
+ errorCollection is not null &&
+ errorCollection.Count > 0 &&
+ errorCollection[0].Win32ErrorCode != 0 &&
+ errorCollection[0].Win32ErrorCode != -1)
{
innerException = new Win32Exception(errorCollection[0].Win32ErrorCode);
}
@@ -279,7 +281,10 @@ internal static SqlException CreateException(
exception.Data.Add("HelpLink.ProdVer", serverVersion);
}
exception.Data.Add("HelpLink.EvtSrc", "MSSQLServer");
- exception.Data.Add("HelpLink.EvtID", errorCollection[0].Number.ToString(CultureInfo.InvariantCulture));
+ if (errorCollection is not null && errorCollection.Count > 0)
+ {
+ exception.Data.Add("HelpLink.EvtID", errorCollection[0].Number.ToString(CultureInfo.InvariantCulture));
+ }
exception.Data.Add("HelpLink.BaseHelpUrl", "https://go.microsoft.com/fwlink");
exception.Data.Add("HelpLink.LinkId", "20476");
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsEnums.cs
index f6df0a82fe..27a87d29e4 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsEnums.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsEnums.cs
@@ -1132,44 +1132,6 @@ public enum SqlCommandColumnEncryptionSetting
///
Disabled,
}
-
- ///
- public enum SqlAuthenticationMethod
- {
- ///
- NotSpecified = 0,
-
- ///
- SqlPassword,
-
- ///
- [Obsolete("ActiveDirectoryPassword is deprecated, use a more secure authentication method. See https://aka.ms/SqlClientEntraIDAuthentication for more details.")]
- ActiveDirectoryPassword,
-
- ///
- ActiveDirectoryIntegrated,
-
- ///
- ActiveDirectoryInteractive,
-
- ///
- ActiveDirectoryServicePrincipal,
-
- ///
- ActiveDirectoryDeviceCodeFlow,
-
- ///
- ActiveDirectoryManagedIdentity,
-
- ///
- ActiveDirectoryMSI,
-
- ///
- ActiveDirectoryDefault,
-
- ///
- ActiveDirectoryWorkloadIdentity
- }
// This enum indicates the state of TransparentNetworkIPResolution
// The first attempt when TNIR is on should be sequential. If the first attempt fails next attempts should be parallel.
internal enum TransparentNetworkResolutionState
@@ -1179,12 +1141,6 @@ internal enum TransparentNetworkResolutionState
ParallelMode
};
- internal class ActiveDirectoryAuthentication
- {
- internal const string AdoClientId = "2fd908ad-0664-4344-b9be-cd3e8b574c38";
- internal const string MSALGetAccessTokenFunctionName = "AcquireToken";
- }
-
// Fields in the first resultset of "sp_describe_parameter_encryption".
// We expect the server to return the fields in the resultset in the same order as mentioned below.
// If the server changes the below order, then transparent parameter encryption will break.
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParser.cs
index 07ab80b96a..23063704ae 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParser.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParser.cs
@@ -4307,7 +4307,8 @@ private TdsOperationStatus TryProcessLoginAck(TdsParserStateObject stateObj, out
private TdsOperationStatus TryProcessFedAuthInfo(TdsParserStateObject stateObj, int tokenLen, out SqlFedAuthInfo sqlFedAuthInfo)
{
sqlFedAuthInfo = null;
- SqlFedAuthInfo tempFedAuthInfo = new SqlFedAuthInfo();
+ string spn = null;
+ string stsUrl = null;
// Skip reading token length, since it has already been read in caller
SqlClientEventSource.Log.TryAdvancedTraceEvent(" FEDAUTHINFO token stream length = {0}", tokenLen);
@@ -4400,15 +4401,15 @@ private TdsOperationStatus TryProcessFedAuthInfo(TdsParserStateObject stateObj,
}
SqlClientEventSource.Log.TryAdvancedTraceEvent(" FedAuthInfoData: {0}", data);
- // store data in tempFedAuthInfo
+ // Store data in temporaries.
switch ((TdsEnums.FedAuthInfoId)id)
{
case TdsEnums.FedAuthInfoId.Spn:
- tempFedAuthInfo.spn = data;
+ spn = data;
break;
case TdsEnums.FedAuthInfoId.Stsurl:
- tempFedAuthInfo.stsurl = data;
+ stsUrl = data;
break;
default:
@@ -4423,15 +4424,16 @@ private TdsOperationStatus TryProcessFedAuthInfo(TdsParserStateObject stateObj,
throw SQL.ParsingErrorLength(ParsingErrorState.FedAuthInfoLengthTooShortForData, tokenLen);
}
- SqlClientEventSource.Log.TryTraceEvent(" Processed FEDAUTHINFO token stream: {0}", tempFedAuthInfo);
- if (string.IsNullOrWhiteSpace(tempFedAuthInfo.stsurl) || string.IsNullOrWhiteSpace(tempFedAuthInfo.spn))
+ if (string.IsNullOrWhiteSpace(spn) || string.IsNullOrWhiteSpace(stsUrl))
{
// We should be receiving both stsurl and spn
SqlClientEventSource.Log.TryTraceEvent(" FEDAUTHINFO token stream does not contain both STSURL and SPN.");
throw SQL.ParsingError(ParsingErrorState.FedAuthInfoDoesNotContainStsurlAndSpn);
}
- sqlFedAuthInfo = tempFedAuthInfo;
+ sqlFedAuthInfo = new(spn, stsUrl);
+ SqlClientEventSource.Log.TryTraceEvent(" Processed FEDAUTHINFO token stream: {0}", sqlFedAuthInfo);
+
return TdsOperationStatus.Done;
}
@@ -9593,11 +9595,11 @@ private int ApplyFeatureExData(TdsEnums.FeatureExtension requestedFeatures,
internal void SendFedAuthToken(SqlFedAuthToken fedAuthToken)
{
Debug.Assert(fedAuthToken != null, "fedAuthToken cannot be null");
- Debug.Assert(fedAuthToken.accessToken != null, "fedAuthToken.accessToken cannot be null");
+ Debug.Assert(fedAuthToken.AccessToken != null, "fedAuthToken.AccessToken cannot be null");
SqlClientEventSource.Log.TryTraceEvent(" Sending federated authentication token");
_physicalStateObj._outputMessageType = TdsEnums.MT_FEDAUTH;
- byte[] accessToken = fedAuthToken.accessToken;
+ byte[] accessToken = fedAuthToken.AccessToken;
// Send total length (length of token plus 4 bytes for the token length field)
// If we were sending a nonce, this would include that length as well
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs
index 8f0bb915c0..16386e1d9d 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs
@@ -13,6 +13,7 @@
using System.Security;
using System.Security.Authentication;
using System.Text;
+using System.Text.Encodings;
using Microsoft.Data.Common;
using Microsoft.Data.Common.ConnectionString;
@@ -128,23 +129,53 @@ internal sealed class SqlLoginAck
internal uint tdsVersion;
}
+ #nullable enable
+
internal sealed class SqlFedAuthInfo
{
- internal string spn;
- internal string stsurl;
+ internal string Spn { get; }
+ internal string StsUrl { get; }
+
+ internal SqlFedAuthInfo(string spn, string stsurl)
+ {
+ Spn = spn;
+ StsUrl = stsurl;
+ }
+
public override string ToString()
{
- return $"STSURL: {stsurl}, SPN: {spn}";
+ return $"SPN: {Spn}, STSURL: {StsUrl}";
}
}
internal sealed class SqlFedAuthToken
{
- internal uint dataLen;
- internal byte[] accessToken;
- internal long expirationFileTime;
+ internal byte[] AccessToken { get; }
+ internal uint DataLen { get; }
+ internal long ExpirationFileTime { get; }
+
+ internal SqlFedAuthToken(
+ byte[] accessToken,
+ long expirationFileTime)
+ {
+ AccessToken = accessToken;
+ DataLen = (uint)AccessToken.Length;
+ ExpirationFileTime = expirationFileTime;
+ }
+
+ ///
+ /// Convert from a SqlAuthenticationToken.
+ ///
+ internal SqlFedAuthToken(SqlAuthenticationToken token)
+ {
+ AccessToken = Encoding.Unicode.GetBytes(token.AccessToken);
+ DataLen = (uint)AccessToken.Length;
+ ExpirationFileTime = token.ExpiresOn.ToFileTime();
+ }
}
+ #nullable disable
+
internal sealed class _SqlMetaData : SqlMetaDataPriv
{
[Flags]
diff --git a/src/Microsoft.Data.SqlClient/tests/CustomConfigurableRetryLogic/CustomRetryLogicProvider.csproj b/src/Microsoft.Data.SqlClient/tests/CustomConfigurableRetryLogic/CustomRetryLogicProvider.csproj
index 1b35e8f80d..f420579f9e 100644
--- a/src/Microsoft.Data.SqlClient/tests/CustomConfigurableRetryLogic/CustomRetryLogicProvider.csproj
+++ b/src/Microsoft.Data.SqlClient/tests/CustomConfigurableRetryLogic/CustomRetryLogicProvider.csproj
@@ -10,9 +10,15 @@
-
-
-
-
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Microsoft.Data.SqlClient/tests/Directory.Build.props b/src/Microsoft.Data.SqlClient/tests/Directory.Build.props
index 71335f36ba..d790614874 100644
--- a/src/Microsoft.Data.SqlClient/tests/Directory.Build.props
+++ b/src/Microsoft.Data.SqlClient/tests/Directory.Build.props
@@ -11,7 +11,6 @@
true
Debug;Release;
AnyCPU;x86;x64
- Project
diff --git a/src/Microsoft.Data.SqlClient/tests/Directory.Packages.props b/src/Microsoft.Data.SqlClient/tests/Directory.Packages.props
index 4a031a49a6..e8d684f944 100644
--- a/src/Microsoft.Data.SqlClient/tests/Directory.Packages.props
+++ b/src/Microsoft.Data.SqlClient/tests/Directory.Packages.props
@@ -14,9 +14,4 @@
-
-
-
-
-
diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AADAuthenticationTests.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AADAuthenticationTests.cs
index f354e6f806..32050a9716 100644
--- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AADAuthenticationTests.cs
+++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AADAuthenticationTests.cs
@@ -49,16 +49,20 @@ private void InvalidCombinationCheck(SqlCredential credential)
Assert.Throws(() => connection.AccessToken = "SampleAccessToken");
}
}
-
- #if NETFRAMEWORK
- // This test is only valid for .NET Framework
///
- /// Tests whether SQL Auth provider is overridden using app.config file.
- /// This use case is only supported for .NET Framework applications, as driver doesn't support reading configuration from appsettings.json file.
- /// In future if need be, appsettings.json support can be added.
+ /// Tests whether a dummy SQL Auth provider is registered due to
+ /// configuration in an app.config file. Only .NET Framework reads
+ /// from the app.config file, so this test is only valid for that
+ /// runtime.
+ ///
+ /// See the app.config file in the same directory as this file.
+ ///
+ /// .NET (Core) reads similar configuration from appsettings.json, but
+ /// our SqlAuthenticationProviderManager does not currently support
+ /// that configuration source.
///
- [Fact]
+ [ConditionalFact(typeof(TestUtility), nameof(TestUtility.IsNetFramework))]
public async Task IsDummySqlAuthenticationProviderSetByDefault()
{
var provider = SqlAuthenticationProvider.GetProvider(SqlAuthenticationMethod.ActiveDirectoryInteractive);
@@ -69,14 +73,13 @@ public async Task IsDummySqlAuthenticationProviderSetByDefault()
var token = await provider.AcquireTokenAsync(null);
Assert.Equal(token.AccessToken, DummySqlAuthenticationProvider.DUMMY_TOKEN_STR);
}
- #endif
[Fact]
public void CustomActiveDirectoryProviderTest()
{
SqlAuthenticationProvider authProvider = new ActiveDirectoryAuthenticationProvider(static (result) => Task.CompletedTask);
SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow, authProvider);
- Assert.Equal(authProvider, SqlAuthenticationProvider.GetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow));
+ Assert.Same(authProvider, SqlAuthenticationProvider.GetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow));
}
[Fact]
@@ -84,7 +87,7 @@ public void CustomActiveDirectoryProviderTest_AppClientId()
{
SqlAuthenticationProvider authProvider = new ActiveDirectoryAuthenticationProvider(Guid.NewGuid().ToString());
SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow, authProvider);
- Assert.Equal(authProvider, SqlAuthenticationProvider.GetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow));
+ Assert.Same(authProvider, SqlAuthenticationProvider.GetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow));
}
[Fact]
@@ -92,7 +95,7 @@ public void CustomActiveDirectoryProviderTest_AppClientId_DeviceFlowCallback()
{
SqlAuthenticationProvider authProvider = new ActiveDirectoryAuthenticationProvider(static (result) => Task.CompletedTask, Guid.NewGuid().ToString());
SqlAuthenticationProvider.SetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow, authProvider);
- Assert.Equal(authProvider, SqlAuthenticationProvider.GetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow));
+ Assert.Same(authProvider, SqlAuthenticationProvider.GetProvider(SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow));
}
}
}
diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AssertExtensions.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AssertExtensions.cs
index 1a37bc6f12..7a2acc1160 100644
--- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AssertExtensions.cs
+++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AssertExtensions.cs
@@ -13,7 +13,7 @@ namespace System
{
public static class AssertExtensions
{
- private static bool IsFullFramework => TestUtility.IsFullFramework;
+ private static bool IsFullFramework => TestUtility.IsNetFramework;
public static void Throws(Action action, string message)
where T : Exception
@@ -36,7 +36,7 @@ public static void Throws(string netCoreParamName, string netFxParamName, Act
IsFullFramework ?
netFxParamName : netCoreParamName;
- if (!TestUtility.NetNative)
+ if (!TestUtility.IsNetNative)
{
Assert.Equal(expectedParamName, exception.ParamName);
}
@@ -57,7 +57,7 @@ public static void Throws(string netCoreParamName, string netFxParamName, Fun
IsFullFramework ?
netFxParamName : netCoreParamName;
- if (!TestUtility.NetNative)
+ if (!TestUtility.IsNetNative)
{
Assert.Equal(expectedParamName, exception.ParamName);
}
@@ -68,7 +68,7 @@ public static T Throws(string paramName, Action action)
{
T exception = Assert.Throws(action);
- if (!TestUtility.NetNative)
+ if (!TestUtility.IsNetNative)
{
Assert.Equal(paramName, exception.ParamName);
}
@@ -89,7 +89,7 @@ public static T Throws(string paramName, Func
-
From 55035f820fdb1ac2b9dbd8e337a943d7821a5d39 Mon Sep 17 00:00:00 2001
From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com>
Date: Fri, 2 Jan 2026 13:54:27 -0400
Subject: [PATCH 05/13] Removed duplicate test job config for macOS.
---
eng/pipelines/dotnet-sqlclient-ci-core.yml | 23 ----------------------
1 file changed, 23 deletions(-)
diff --git a/eng/pipelines/dotnet-sqlclient-ci-core.yml b/eng/pipelines/dotnet-sqlclient-ci-core.yml
index dd11491fdc..bf18ff2f2d 100644
--- a/eng/pipelines/dotnet-sqlclient-ci-core.yml
+++ b/eng/pipelines/dotnet-sqlclient-ci-core.yml
@@ -607,26 +607,3 @@ stages:
UserManagedIdentityClientId: $(UserManagedIdentityClientId)
LocalDbAppName: $(LocalDbAppName)
LocalDbSharedInstanceName: $(LocalDbSharedInstanceName)
-
- # Self hosted SQL Server on Mac
- mac_sql_22:
- pool: Azure Pipelines
- hostedPool: true
- images:
- MacOSLatest_Sql22: macos-latest
- TargetFrameworks: ${{parameters.targetFrameworksUnix }}
- netcoreVersionTestUtils: ${{parameters.netcoreVersionTestUtils }}
- buildPlatforms: [AnyCPU]
- testSets: ${{parameters.testSets }}
- useManagedSNI: [true]
- codeCovTargetFrameworks: ${{parameters.codeCovTargetFrameworks }}
- configSqlFor: local
- operatingSystem: Mac
- configProperties:
- # config.json properties
- TCPConnectionString: $(SQL_TCP_CONN_STRING)
- NPConnectionString: $(SQL_NP_CONN_STRING)
- SupportsIntegratedSecurity: false
- ManagedIdentitySupported: false
- LocalDbAppName: $(LocalDbAppName)
- LocalDbSharedInstanceName: $(LocalDbSharedInstanceName)
From 6cefed9239eb431d62da6303a5320fdd87ecbfef Mon Sep 17 00:00:00 2001
From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com>
Date: Fri, 2 Jan 2026 14:23:42 -0400
Subject: [PATCH 06/13] Converted all Azure Pipelines template references to
absolute paths.
---
.../jobs/build-signed-package-job.yml | 20 +++++++-------
.../templates/jobs/ci-build-nugets-job.yml | 12 ++++-----
.../templates/jobs/ci-code-coverage-job.yml | 4 +--
.../templates/jobs/ci-run-tests-job.yml | 16 ++++++------
.../jobs/run-tests-package-reference-job.yml | 14 +++++-----
.../jobs/validate-signed-package-job.yml | 2 +-
.../templates/stages/ci-run-tests-stage.yml | 4 +--
.../templates/steps/ci-prebuild-step.yml | 8 +++---
.../templates/steps/ci-project-build-step.yml | 4 +--
.../steps/configure-sql-server-step.yml | 6 ++---
.../common/templates/steps/pre-build-step.yml | 6 ++---
eng/pipelines/dotnet-sqlclient-ci-core.yml | 26 +++++++++----------
.../dotnet-sqlclient-signing-pipeline.yml | 6 ++---
eng/pipelines/jobs/build-akv-official-job.yml | 16 ++++++------
eng/pipelines/libraries/build-variables.yml | 4 +--
.../libraries/mds-validation-variables.yml | 4 +--
eng/pipelines/libraries/variables.yml | 2 +-
.../build-abstractions-package-ci-stage.yml | 8 +++---
.../stages/build-azure-package-ci-stage.yml | 8 +++---
.../stages/stress-tests-ci-stage.yml | 6 ++---
20 files changed, 87 insertions(+), 89 deletions(-)
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..f11e8f0396 100644
--- a/eng/pipelines/common/templates/jobs/build-signed-package-job.yml
+++ b/eng/pipelines/common/templates/jobs/build-signed-package-job.yml
@@ -25,7 +25,7 @@ jobs:
type: windows # read more about custom job pool types at https://aka.ms/obpipelines/yaml/jobs
variables:
- - template: ../../../libraries/variables.yml@self
+ - template: /eng/pipelines/libraries/variables.yml@self
- ${{ if parameters.isPreview }}:
- name: abstractionsPackageVersion
value: $(abstractionsPackagePreviewVersion)
@@ -50,12 +50,12 @@ jobs:
msbuildArguments: -t:BuildTools
# Perform analysis before building, since this step will clobber build output.
- - template: ../steps/code-analyze-step.yml@self
+ - template: /eng/pipelines/common/templates/steps/code-analyze-step.yml@self
# Update the root NuGet.config to use the packages/ directory as a source.
# When we build MDS in Package mode, it depends on the Abstractions package,
# which we will build and package into packages/.
- - template: ../steps/update-nuget-config-local-feed-step.yml
+ - template: /eng/pipelines/common/templates/steps/update-nuget-config-local-feed-step.yml@self
parameters:
packagePath: $(REPOROOT)/packages
@@ -68,21 +68,21 @@ jobs:
msbuildArguments: -t:BuildAbstractions
# Build MDS in Package mode, producing signed DLLs.
- - template: ../steps/build-all-configurations-signed-dlls-step.yml@self
+ - template: /eng/pipelines/common/templates/steps/build-all-configurations-signed-dlls-step.yml@self
parameters:
# These variables are sourced from common-variables.yml.
abstractionsAssemblyFileVersion: $(abstractionsAssemblyFileVersion)
abstractionsPackageVersion: $(abstractionsPackageVersion)
- configuration: $(Configuration)
+ buildConfiguration: $(Configuration)
mdsAssemblyFileVersion: $(mdsAssemblyFileVersion)
mdsPackageVersion: $(mdsPackageVersion)
referenceType: Package
- - template: ../steps/esrp-code-signing-step.yml@self
+ - template: /eng/pipelines/common/templates/steps/esrp-code-signing-step.yml@self
parameters:
artifactType: dll
- - template: ../steps/generate-nuget-package-step.yml@self
+ - template: /eng/pipelines/common/templates/steps/generate-nuget-package-step.yml@self
parameters:
buildConfiguration: Release
displayName: 'Create MDS NuGet Package'
@@ -93,17 +93,17 @@ jobs:
properties: 'AbstractionsPackageVersion=$(abstractionsPackageVersion)'
referenceType: Package
- - template: ../steps/esrp-code-signing-step.yml@self
+ - template: /eng/pipelines/common/templates/steps/esrp-code-signing-step.yml@self
parameters:
artifactType: pkg
- - template: ../steps/copy-dlls-for-test-step.yml@self
+ - template: /eng/pipelines/common/templates/steps/copy-dlls-for-test-step.yml@self
parameters:
buildConfiguration: Release
product: MDS
# Publish symbols to servers
- - template: ../steps/publish-symbols-step.yml@self
+ - template: /eng/pipelines/common/templates/steps/publish-symbols-step.yml@self
parameters:
buildConfiguration: Release
publishSymbols: ${{ parameters['PublishSymbols'] }}
diff --git a/eng/pipelines/common/templates/jobs/ci-build-nugets-job.yml b/eng/pipelines/common/templates/jobs/ci-build-nugets-job.yml
index da83c2077c..b490c2027b 100644
--- a/eng/pipelines/common/templates/jobs/ci-build-nugets-job.yml
+++ b/eng/pipelines/common/templates/jobs/ci-build-nugets-job.yml
@@ -62,7 +62,7 @@ jobs:
- msbuild
variables:
- - template: ../../../libraries/ci-build-variables.yml@self
+ - template: /eng/pipelines/libraries/ci-build-variables.yml@self
steps:
- ${{ if ne(parameters.prebuildSteps, '') }}:
@@ -79,12 +79,12 @@ jobs:
targetPath: $(packagePath)
# Note that packages/ will have been created by the above step, which is a
# pre-requisite for configuring NuGet.
- - template: ../steps/ci-prebuild-step.yml@self
+ - template: /eng/pipelines/common/templates/steps/ci-prebuild-step.yml@self
parameters:
debug: ${{ parameters.debug }}
referenceType: ${{ parameters.referenceType }}
- - template: ../steps/ci-project-build-step.yml@self
+ - template: /eng/pipelines/common/templates/steps/ci-project-build-step.yml@self
parameters:
platform: ${{ parameters.platform }}
buildConfiguration: ${{ parameters.buildConfiguration }}
@@ -93,7 +93,7 @@ jobs:
build: MDS
abstractionsPackageVersion: ${{parameters.abstractionsPackageVersion}}
- - template: ../steps/generate-nuget-package-step.yml@self
+ - template: /eng/pipelines/common/templates/steps/generate-nuget-package-step.yml@self
parameters:
buildConfiguration: ${{ parameters.buildConfiguration }}
displayName: 'Create MDS NuGet Package'
@@ -104,7 +104,7 @@ jobs:
properties: 'AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }}'
referenceType: ${{ parameters.referenceType }}
- - template: ../steps/ci-project-build-step.yml@self
+ - template: /eng/pipelines/common/templates/steps/ci-project-build-step.yml@self
parameters:
platform: ${{ parameters.platform }}
buildConfiguration: ${{ parameters.buildConfiguration }}
@@ -113,7 +113,7 @@ jobs:
build: AKV
mdsPackageVersion: ${{parameters.mdsPackageVersion}}
- - template: ../steps/generate-nuget-package-step.yml@self
+ - template: /eng/pipelines/common/templates/steps/generate-nuget-package-step.yml@self
parameters:
buildConfiguration: ${{ parameters.buildConfiguration }}
displayName: 'Create AKV NuGet Package'
diff --git a/eng/pipelines/common/templates/jobs/ci-code-coverage-job.yml b/eng/pipelines/common/templates/jobs/ci-code-coverage-job.yml
index de1397f680..9cda6aed12 100644
--- a/eng/pipelines/common/templates/jobs/ci-code-coverage-job.yml
+++ b/eng/pipelines/common/templates/jobs/ci-code-coverage-job.yml
@@ -59,12 +59,12 @@ jobs:
- pwsh: 'Get-ChildItem env: | Sort-Object Name'
displayName: '[Debug] List Environment Variables'
- - template: ../steps/ensure-dotnet-version.yml@self
+ - template: /eng/pipelines/common/templates/steps/ensure-dotnet-version.yml@self
parameters:
packageType: sdk
version: '10.0'
- - template: ../steps/ensure-dotnet-version.yml@self
+ - template: /eng/pipelines/common/templates/steps/ensure-dotnet-version.yml@self
parameters:
packageType: runtime
version: '9.0'
diff --git a/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml b/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml
index fa95ea52b0..d68007e410 100644
--- a/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml
+++ b/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml
@@ -158,7 +158,7 @@ jobs:
displayName: 'Verify Password'
- ${{ if ne(parameters.configProperties, '{}') }}:
- - template: ../steps/update-config-file-step.yml@self # update config.json file
+ - template: /eng/pipelines/common/templates/steps/update-config-file-step.yml@self # update config.json file
parameters:
debug: ${{ parameters.debug }}
UseManagedSNIOnWindows: ${{ parameters.usemanagedSNI }}
@@ -233,7 +233,7 @@ jobs:
displayName: 'Start Sql Browser'
condition: eq(variables['Agent.OS'], 'Windows_NT')
- ${{ elseif eq(parameters.configSqlFor, 'local') }}:
- - template: ../steps/configure-sql-server-step.yml@self # configure SQL Server
+ - template: /eng/pipelines/common/templates/steps/configure-sql-server-step.yml@self # configure SQL Server
parameters:
operatingSystem: ${{ parameters.operatingSystem }}
netcoreVersionTestUtils: ${{ parameters.netcoreVersionTestUtils }}
@@ -264,7 +264,7 @@ jobs:
${{ if parameters.configProperties.FileStreamDirectory }}:
fileStreamDirectory: ${{ parameters.configProperties.FileStreamDirectory }}
- - template: ../steps/build-all-tests-step.yml@self # build tests
+ - template: /eng/pipelines/common/templates/steps/build-all-tests-step.yml@self # build tests
parameters:
targetFramework: ${{ parameters.targetFramework }}
buildConfiguration: ${{ parameters.buildConfiguration }}
@@ -277,7 +277,7 @@ jobs:
OSGroup: Unix
- ${{ if eq(parameters.enableX64Test, true) }}: # run native tests
- - template: ../steps/run-all-tests-step.yml@self # run tests
+ - template: /eng/pipelines/common/templates/steps/run-all-tests-step.yml@self # run tests
parameters:
debug: ${{ parameters.debug }}
targetFramework: ${{ parameters.targetFramework }}
@@ -298,7 +298,7 @@ jobs:
# forever and needs a serious rethinking).
- ${{ if ne(variables['dotnetx86RootPath'], '') }}:
- ${{ if startswith(parameters.targetFramework, 'net4')}}:
- - template: ../steps/ensure-dotnet-version.yml
+ - template: /eng/pipelines/common/templates/steps/ensure-dotnet-version.yml
parameters:
installDir: "$(dotnetx86RootPath)"
packageType: "sdk"
@@ -313,14 +313,14 @@ jobs:
echo %TrimmedFrameworkVersion%
echo ##vso[task.setvariable variable=netVersionX86]%TrimmedFrameworkVersion%
displayName: "Trim dotnet version"
- - template: ../steps/ensure-dotnet-version.yml
+ - template: /eng/pipelines/common/templates/steps/ensure-dotnet-version.yml
parameters:
installDir: "$(dotnetx86RootPath)"
packageType: "sdk"
usePreview: "false"
version: $(netVersionX86)
- - template: ../steps/run-all-tests-step.yml@self
+ - template: /eng/pipelines/common/templates/steps/run-all-tests-step.yml@self
parameters:
debug: ${{ parameters.debug }}
targetFramework: ${{ parameters.targetFramework }}
@@ -332,7 +332,7 @@ jobs:
operatingSystem: ${{ parameters.operatingSystem }}
- ${{ if and(eq(parameters.publishTestResults, true), eq(parameters.referenceType, 'Project')) }}: # publish test results if build type is project
- - template: ../steps/publish-test-results-step.yml@self
+ - template: /eng/pipelines/common/templates/steps/publish-test-results-step.yml@self
parameters:
debug: ${{ parameters.debug }}
targetFramework: ${{ parameters.targetFramework }}
diff --git a/eng/pipelines/common/templates/jobs/run-tests-package-reference-job.yml b/eng/pipelines/common/templates/jobs/run-tests-package-reference-job.yml
index 61cb1db200..3817fcb483 100644
--- a/eng/pipelines/common/templates/jobs/run-tests-package-reference-job.yml
+++ b/eng/pipelines/common/templates/jobs/run-tests-package-reference-job.yml
@@ -41,34 +41,34 @@ jobs:
vmImage: 'ADO-MMS22-SQL19'
variables: # More settings at https://aka.ms/obpipelines/yaml/jobs
- - template: ../../../libraries/mds-validation-variables.yml@self
+ - template: /eng/pipelines/libraries/mds-validation-variables.yml@self
steps:
- - template: ../steps/pre-build-step.yml
+ - template: /eng/pipelines/common/templates/steps/pre-build-step.yml
- ${{parameters.downloadPackageStep }}
- - template: ../steps/update-nuget-config-local-feed-step.yml
+ - template: /eng/pipelines/common/templates/steps/update-nuget-config-local-feed-step.yml
parameters:
packagePath: $(Pipeline.Workspace)\${{parameters.packageFolderName }}
- - template: ../steps/update-config-file-step.yml
+ - template: /eng/pipelines/common/templates/steps/update-config-file-step.yml
parameters:
TCPConnectionString: $(SQL_TCP_CONN_STRING)
NPConnectionString: $(SQL_NP_CONN_STRING)
SupportsIntegratedSecurity: false
- - template: ../steps/prepare-test-db-step.yml
+ - template: /eng/pipelines/common/templates/steps/prepare-test-db-step.yml
# build & test
- - template: ../steps/build-and-run-tests-netfx-step.yml
+ - template: /eng/pipelines/common/templates/steps/build-and-run-tests-netfx-step.yml
parameters:
referenceType: Package
buildConfiguration: Release
${{ if parameters.isPreview }}:
mdsPackageVersion: $(previewMdsPackageVersion)
- - template: ../steps/build-and-run-tests-netcore-step.yml
+ - template: /eng/pipelines/common/templates/steps/build-and-run-tests-netcore-step.yml
parameters:
referenceType: Package
buildConfiguration: Release
diff --git a/eng/pipelines/common/templates/jobs/validate-signed-package-job.yml b/eng/pipelines/common/templates/jobs/validate-signed-package-job.yml
index 030199e72b..b734f70de7 100644
--- a/eng/pipelines/common/templates/jobs/validate-signed-package-job.yml
+++ b/eng/pipelines/common/templates/jobs/validate-signed-package-job.yml
@@ -40,7 +40,7 @@ jobs:
vmImage: 'ADO-MMS22-SQL19'
variables: # More settings at https://aka.ms/obpipelines/yaml/jobs
- - template: ../../../libraries/mds-validation-variables.yml@self
+ - template: /eng/pipelines/libraries/mds-validation-variables.yml@self
- name: pathToDownloadedNuget # path to the downloaded nuget files
value: $(Pipeline.Workspace)\${{parameters.packageFolderName }}
diff --git a/eng/pipelines/common/templates/stages/ci-run-tests-stage.yml b/eng/pipelines/common/templates/stages/ci-run-tests-stage.yml
index 6d70f29900..febcbe83df 100644
--- a/eng/pipelines/common/templates/stages/ci-run-tests-stage.yml
+++ b/eng/pipelines/common/templates/stages/ci-run-tests-stage.yml
@@ -68,7 +68,7 @@ stages:
- ${{ each platform in config.value.buildPlatforms }}:
- ${{ each testSet in config.value.TestSets }}:
- ${{ if contains(targetFramework, 'net4') }}: # .NET Framework
- - template: ../jobs/ci-run-tests-job.yml@self
+ - template: /eng/pipelines/common/templates/jobs/ci-run-tests-job.yml@self
parameters:
debug: ${{ parameters.debug }}
buildConfiguration: ${{ parameters.buildConfiguration }}
@@ -101,7 +101,7 @@ stages:
enableX64Test: false
- ${{ else }}: # .NET
- ${{ each useManagedSNI in config.value.useManagedSNI }}:
- - template: ../jobs/ci-run-tests-job.yml@self
+ - template: /eng/pipelines/common/templates/jobs/ci-run-tests-job.yml@self
parameters:
debug: ${{ parameters.debug }}
buildConfiguration: ${{ parameters.buildConfiguration }}
diff --git a/eng/pipelines/common/templates/steps/ci-prebuild-step.yml b/eng/pipelines/common/templates/steps/ci-prebuild-step.yml
index 6762da1829..2ed7f4c2fb 100644
--- a/eng/pipelines/common/templates/steps/ci-prebuild-step.yml
+++ b/eng/pipelines/common/templates/steps/ci-prebuild-step.yml
@@ -15,19 +15,19 @@ parameters:
- Package
steps:
-- template: ensure-dotnet-version.yml
+- template: /eng/pipelines/common/templates/steps/ensure-dotnet-version.yml
parameters:
packageType: sdk
usePreview: false
version: 10.0
-- template: ensure-dotnet-version.yml
+- template: /eng/pipelines/common/templates/steps/ensure-dotnet-version.yml
parameters:
packageType: runtime
usePreview: false
version: 9.0
-- template: ensure-dotnet-version.yml
+- template: /eng/pipelines/common/templates/steps/ensure-dotnet-version.yml
parameters:
packageType: runtime
usePreview: false
@@ -39,7 +39,7 @@ steps:
displayName: 'List Environment Variables [debug]'
- ${{if eq(parameters.referenceType, 'Package')}}:
- - template: update-nuget-config-local-feed-step.yml@self
+ - template: /eng/pipelines/common/templates/steps/update-nuget-config-local-feed-step.yml@self
parameters:
debug: ${{ parameters.debug }}
packagePath: $(Build.SourcesDirectory)/packages
diff --git a/eng/pipelines/common/templates/steps/ci-project-build-step.yml b/eng/pipelines/common/templates/steps/ci-project-build-step.yml
index 1424fdf2ea..7b93c0c1c2 100644
--- a/eng/pipelines/common/templates/steps/ci-project-build-step.yml
+++ b/eng/pipelines/common/templates/steps/ci-project-build-step.yml
@@ -53,12 +53,12 @@ parameters:
default: ''
steps:
-- template: ./ensure-dotnet-version.yml@self
+- template: /eng/pipelines/common/templates/ensure-dotnet-version.yml@self
parameters:
packageType: 'sdk'
version: '10.0'
-- template: ./ensure-dotnet-version.yml@self
+- template: /eng/pipelines/common/templates/ensure-dotnet-version.yml@self
parameters:
packageType: 'runtime'
version: '9.0'
diff --git a/eng/pipelines/common/templates/steps/configure-sql-server-step.yml b/eng/pipelines/common/templates/steps/configure-sql-server-step.yml
index bfb89e34b9..a11dd38409 100644
--- a/eng/pipelines/common/templates/steps/configure-sql-server-step.yml
+++ b/eng/pipelines/common/templates/steps/configure-sql-server-step.yml
@@ -71,7 +71,7 @@ parameters:
steps:
- ${{ if eq(parameters.operatingSystem, 'Windows') }}:
# windows only steps
- - template: configure-sql-server-win-step.yml@self
+ - template: /eng/pipelines/common/templates/steps/configure-sql-server-win-step.yml@self
parameters:
instanceName: ${{parameters.instanceName}}
user: ${{parameters.user}}
@@ -89,13 +89,13 @@ steps:
- ${{ elseif eq(parameters.operatingSystem, 'Linux') }}:
# Linux only steps
- - template: configure-sql-server-linux-step.yml@self
+ - template: /eng/pipelines/common/templates/steps/configure-sql-server-linux-step.yml@self
parameters:
password: ${{parameters.password}}
- ${{ elseif eq(parameters.operatingSystem, 'Mac') }}:
# macOS only steps
- - template: configure-sql-server-macos-step.yml@self
+ - template: /eng/pipelines/common/templates/steps/configure-sql-server-macos-step.yml@self
parameters:
password: ${{parameters.password}}
diff --git a/eng/pipelines/common/templates/steps/pre-build-step.yml b/eng/pipelines/common/templates/steps/pre-build-step.yml
index cfcd9a46f8..c6cb6ca308 100644
--- a/eng/pipelines/common/templates/steps/pre-build-step.yml
+++ b/eng/pipelines/common/templates/steps/pre-build-step.yml
@@ -4,18 +4,18 @@
# See the LICENSE file in the project root for more information. #
#################################################################################
steps:
-- template: ./ensure-dotnet-version.yml@self
+- template: /eng/pipelines/common/templates/steps/ensure-dotnet-version.yml@self
parameters:
packageType: 'sdk'
version: '10.0'
usePreview: false
-- template: ./ensure-dotnet-version.yml@self
+- template: /eng/pipelines/common/templates/steps/ensure-dotnet-version.yml@self
parameters:
packageType: 'runtime'
version: '9.0'
-- template: ./ensure-dotnet-version.yml@self
+- template: /eng/pipelines/common/templates/steps/ensure-dotnet-version.yml@self
parameters:
packageType: 'runtime'
version: '8.0'
diff --git a/eng/pipelines/dotnet-sqlclient-ci-core.yml b/eng/pipelines/dotnet-sqlclient-ci-core.yml
index bf18ff2f2d..0db276944b 100644
--- a/eng/pipelines/dotnet-sqlclient-ci-core.yml
+++ b/eng/pipelines/dotnet-sqlclient-ci-core.yml
@@ -104,7 +104,7 @@ parameters:
default: true
variables:
- - template: libraries/ci-build-variables.yml@self
+ - template: /eng/pipelines/libraries/ci-build-variables.yml@self
- name: abstractionsArtifactName
value: Abstractions.Artifact
@@ -119,7 +119,7 @@ stages:
# Build the Abstractions package, and publish it to the pipeline artifacts
# under the given artifact name.
- - template: stages/build-abstractions-package-ci-stage.yml@self
+ - template: /eng/pipelines/stages/build-abstractions-package-ci-stage.yml@self
parameters:
buildConfiguration: ${{ parameters.buildConfiguration }}
abstractionsPackageVersion: $(abstractionsPackageVersion)
@@ -140,7 +140,7 @@ stages:
dependsOn: []
jobs:
- - template: common/templates/jobs/ci-build-nugets-job.yml@self
+ - template: /eng/pipelines/common/templates/jobs/ci-build-nugets-job.yml@self
parameters:
buildConfiguration: ${{ parameters.buildConfiguration }}
referenceType: ${{ parameters.referenceType }}
@@ -150,14 +150,14 @@ stages:
mdsArtifactName: $(mdsArtifactName)
${{if ne(parameters.SNIVersion, '')}}:
prebuildSteps:
- - template: common/templates/steps/override-sni-version.yml@self
+ - template: /eng/pipelines/common/templates/steps/override-sni-version.yml@self
parameters:
SNIVersion: ${{parameters.SNIVersion}}
SNIValidationFeed: ${{parameters.SNIValidationFeed}}
# Build the Azure package, and publish it to the pipeline artifacts under the
# given artifact name.
- - template: stages/build-azure-package-ci-stage.yml@self
+ - template: /eng/pipelines/stages/build-azure-package-ci-stage.yml@self
parameters:
abstractionsArtifactName: $(abstractionsArtifactName)
abstractionsPackageVersion: $(abstractionsPackageVersion)
@@ -175,7 +175,7 @@ stages:
# Run the stress tests, if desired.
- ${{ if eq(parameters.enableStressTests, true) }}:
- - template: stages/stress-tests-ci-stage.yml@self
+ - template: /eng/pipelines/stages/stress-tests-ci-stage.yml@self
parameters:
buildConfiguration: ${{ parameters.buildConfiguration }}
dependsOn: [build_mds_akv_packages_stage]
@@ -185,7 +185,7 @@ stages:
verbosity: 'detailed'
# Run the MDS and AKV tests.
- - template: common/templates/stages/ci-run-tests-stage.yml@self
+ - template: /eng/pipelines/common/templates/stages/ci-run-tests-stage.yml@self
parameters:
debug: ${{ parameters.debug }}
buildConfiguration: ${{ parameters.buildConfiguration }}
@@ -197,8 +197,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.
@@ -210,21 +208,21 @@ stages:
prebuildSteps: # steps to run prior to building and running tests on each job
- ${{if ne(parameters.SNIVersion, '')}}:
- - template: common/templates/steps/override-sni-version.yml@self
+ - template: /eng/pipelines/common/templates/steps/override-sni-version.yml@self
parameters:
SNIVersion: ${{parameters.SNIVersion}}
SNIValidationFeed: ${{parameters.SNIValidationFeed}}
- - template: common/templates/steps/ci-prebuild-step.yml@self
+ - template: /eng/pipelines/common/templates/steps/ci-prebuild-step.yml@self
parameters:
debug: ${{ parameters.debug }}
referenceType: ${{ parameters.referenceType }}
# Include the code coverage job if the build type is Project.
- ${{ if eq(parameters.buildType, 'Project') }}:
+ ${{ if eq(parameters.referenceType, 'Project') }}:
# Jobs to run as part of the tests stage, after the tests are done.
postTestJobs:
- - template: common/templates/jobs/ci-code-coverage-job.yml@self
+ - template: /eng/pipelines/common/templates/jobs/ci-code-coverage-job.yml@self
parameters:
debug: ${{ parameters.debug }}
image: ADO-MMS22-CodeCov
@@ -528,7 +526,7 @@ stages:
hostedPool: true
images:
MacOSLatest_Sql22: macos-latest
- TargetFrameworks: ${{parameters.targetFrameworksLinux }}
+ TargetFrameworks: ${{parameters.targetFrameworksUnix }}
netcoreVersionTestUtils: ${{parameters.netcoreVersionTestUtils }}
buildPlatforms: [AnyCPU]
testSets: ${{parameters.testSets }}
diff --git a/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml b/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml
index a05cc5401f..c759e8937b 100644
--- a/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml
+++ b/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml
@@ -145,7 +145,7 @@ extends:
- stage: buildMDS
displayName: 'Build MDS'
jobs:
- - template: eng/pipelines/common/templates/jobs/build-signed-package-job.yml@self
+ - template: /eng/pipelines/common/templates/jobs/build-signed-package-job.yml@self
parameters:
symbolsFolder: $(symbolsFolder)
softwareFolder: $(softwareFolder)
@@ -156,7 +156,7 @@ extends:
displayName: 'MDS Package Validation'
dependsOn: buildMDS
jobs:
- - template: eng/pipelines/common/templates/jobs/validate-signed-package-job.yml@self
+ - template: /eng/pipelines/common/templates/jobs/validate-signed-package-job.yml@self
parameters:
packageFolderName: $(packageFolderName)
isPreview: ${{ parameters['isPreview'] }}
@@ -167,7 +167,7 @@ extends:
displayName: 'Download NuGet Package'
# Disabling as of 10/15/2025 due to OneBranch apparently disallowing MSBuild tasks in validation stages.
-# - template: eng/pipelines/common/templates/jobs/run-tests-package-reference-job.yml@self
+# - template: /eng/pipelines/common/templates/jobs/run-tests-package-reference-job.yml@self
# parameters:
# packageFolderName: $(packageFolderName)
# isPreview: ${{ parameters['isPreview'] }}
diff --git a/eng/pipelines/jobs/build-akv-official-job.yml b/eng/pipelines/jobs/build-akv-official-job.yml
index 12909a04e0..5b2f15d3f4 100644
--- a/eng/pipelines/jobs/build-akv-official-job.yml
+++ b/eng/pipelines/jobs/build-akv-official-job.yml
@@ -82,7 +82,7 @@ jobs:
ob_outputDirectory: '$(ARTIFACT_PATH)'
steps:
- - template: ../steps/script-output-environment-variables-step.yml@self
+ - template: /eng/pipelines/steps/script-output-environment-variables-step.yml@self
- powershell: |
$jsonParams = '${{ convertToJson(parameters) }}' -replace '\\', '\\'
@@ -91,13 +91,13 @@ jobs:
# Perform analysis before building, since this step will clobber build
# output.
- - template: ../steps/roslyn-analyzers-akv-step.yml@self
+ - template: /eng/pipelines/steps/roslyn-analyzers-akv-step.yml@self
parameters:
akvPackageVersion: '${{ parameters.akvPackageVersion }}'
buildConfiguration: '${{ parameters.buildConfiguration }}'
mdsPackageVersion: '${{ parameters.mdsPackageVersion }}'
- - template: ../steps/compound-build-akv-step.yml@self
+ - template: /eng/pipelines/steps/compound-build-akv-step.yml@self
parameters:
akvAssemblyFileVersion: '${{ parameters.akvAssemblyFileVersion }}'
akvPackageVersion: '${{ parameters.akvPackageVersion }}'
@@ -105,7 +105,7 @@ jobs:
mdsPackageVersion: '${{ parameters.mdsPackageVersion }}'
- ${{ each targetFramework in parameters.targetFrameworks }}:
- - template: ../steps/compound-extract-akv-apiscan-files-step.yml
+ - template: /eng/pipelines/steps/compound-extract-akv-apiscan-files-step.yml@self
parameters:
buildConfiguration: '${{ parameters.buildConfiguration }}'
dllPath: '${{ parameters.apiScanDllPath }}'
@@ -113,7 +113,7 @@ jobs:
referenceType: Package
targetFramework: '${{ targetFramework }}'
- - template: ../steps/compound-esrp-code-signing-step.yml@self
+ - template: /eng/pipelines/steps/compound-esrp-code-signing-step.yml@self
parameters:
appRegistrationClientId: '${{ parameters.signingAppRegistrationClientId }}'
appRegistrationTenantId: '${{ parameters.signingAppRegistrationTenantId }}'
@@ -123,7 +123,7 @@ jobs:
esrpClientId: '${{ parameters.signingEsrpClientId }}'
esrpConnectedServiceName: '${{ parameters.signingEsrpConnectedServiceName }}'
- - template: ../steps/compound-nuget-pack-step.yml@self
+ - template: /eng/pipelines/steps/compound-nuget-pack-step.yml@self
parameters:
buildConfiguration: '${{ parameters.buildConfiguration }}'
generateSymbolsPackage: true # Always generate symbols, even if they are not published
@@ -133,7 +133,7 @@ jobs:
referenceType: Package
properties: MdsPackageVersion=${{ parameters.mdsPackageVersion }}
- - template: ../steps/compound-esrp-code-signing-step.yml@self
+ - template: /eng/pipelines/steps/compound-esrp-code-signing-step.yml@self
parameters:
appRegistrationClientId: '${{ parameters.signingAppRegistrationClientId }}'
appRegistrationTenantId: '${{ parameters.signingAppRegistrationTenantId }}'
@@ -144,7 +144,7 @@ jobs:
esrpConnectedServiceName: '${{ parameters.signingEsrpConnectedServiceName }}'
- ${{ if parameters.publishSymbols }}:
- - template: ../steps/compound-publish-symbols-step.yml@self
+ - template: /eng/pipelines/steps/compound-publish-symbols-step.yml@self
parameters:
artifactName: 'akv_symbols_$(System.TeamProject)_$(Build.Repository.Name)_$(Build.SourceBranchName)_${{ parameters.akvPackageVersion }}_$(System.TimelineId)'
azureSubscription: '${{ parameters.symbolsAzureSubscription }}'
diff --git a/eng/pipelines/libraries/build-variables.yml b/eng/pipelines/libraries/build-variables.yml
index 3dd87fe7d7..a5b9fc99c4 100644
--- a/eng/pipelines/libraries/build-variables.yml
+++ b/eng/pipelines/libraries/build-variables.yml
@@ -5,5 +5,5 @@
#################################################################################
variables:
- - template: common-variables.yml@self
- - template: mds-variables.yml@self
+ - template: /eng/pipelines/libraries/common-variables.yml@self
+ - template: /eng/pipelines/libraries/mds-variables.yml@self
diff --git a/eng/pipelines/libraries/mds-validation-variables.yml b/eng/pipelines/libraries/mds-validation-variables.yml
index 93dc0804ff..f465c9c390 100644
--- a/eng/pipelines/libraries/mds-validation-variables.yml
+++ b/eng/pipelines/libraries/mds-validation-variables.yml
@@ -5,8 +5,8 @@
#################################################################################
variables:
- - template: common-variables.yml@self
- - template: mds-variables.yml@self
+ - template: /eng/pipelines/libraries/common-variables.yml@self
+ - template: /eng/pipelines/libraries/mds-variables.yml@self
- name: TempFolderName # extract the nuget package here
value: temp
diff --git a/eng/pipelines/libraries/variables.yml b/eng/pipelines/libraries/variables.yml
index 57894459d3..03cf141318 100644
--- a/eng/pipelines/libraries/variables.yml
+++ b/eng/pipelines/libraries/variables.yml
@@ -5,7 +5,7 @@
#################################################################################
variables:
- - template: build-variables.yml@self
+ - template: /eng/pipelines/libraries/build-variables.yml@self
# onebranch template variables
- name: ob_outputDirectory
value: '$(artifactDirectory)' # this directory is uploaded to pipeline artifacts, reddog and cloudvault. More info at https://aka.ms/obpipelines/artifacts
diff --git a/eng/pipelines/stages/build-abstractions-package-ci-stage.yml b/eng/pipelines/stages/build-abstractions-package-ci-stage.yml
index 0d84dc6ccb..b626e425f2 100644
--- a/eng/pipelines/stages/build-abstractions-package-ci-stage.yml
+++ b/eng/pipelines/stages/build-abstractions-package-ci-stage.yml
@@ -63,7 +63,7 @@ stages:
# ------------------------------------------------------------------------
# Build and test on Linux.
- - template: ../jobs/test-abstractions-package-ci-job.yml@self
+ - template: /eng/pipelines/jobs/test-abstractions-package-ci-job.yml@self
parameters:
jobNameSuffix: linux
displayNamePrefix: Linux
@@ -77,7 +77,7 @@ stages:
# ------------------------------------------------------------------------
# Build and test on Windows
- - template: ../jobs/test-abstractions-package-ci-job.yml@self
+ - template: /eng/pipelines/jobs/test-abstractions-package-ci-job.yml@self
parameters:
jobNameSuffix: windows
displayNamePrefix: Win
@@ -91,7 +91,7 @@ stages:
# ------------------------------------------------------------------------
# Build and test on macOS.
- - template: ../jobs/test-abstractions-package-ci-job.yml
+ - template: /eng/pipelines/jobs/test-abstractions-package-ci-job.yml@self
parameters:
jobNameSuffix: macos
displayNamePrefix: macOS
@@ -105,7 +105,7 @@ stages:
# ------------------------------------------------------------------------
# Create and publish the NuGet package.
- - template: ../jobs/pack-abstractions-package-ci-job.yml@self
+ - template: /eng/pipelines/jobs/pack-abstractions-package-ci-job.yml@self
parameters:
artifactName: ${{ parameters.artifactName }}
buildConfiguration: ${{ parameters.buildConfiguration }}
diff --git a/eng/pipelines/stages/build-azure-package-ci-stage.yml b/eng/pipelines/stages/build-azure-package-ci-stage.yml
index 90beb30caf..ae5606806f 100644
--- a/eng/pipelines/stages/build-azure-package-ci-stage.yml
+++ b/eng/pipelines/stages/build-azure-package-ci-stage.yml
@@ -94,7 +94,7 @@ stages:
# ------------------------------------------------------------------------
# Build and test on Linux.
- - template: ../jobs/test-azure-package-ci-job.yml@self
+ - template: /eng/pipelines/jobs/test-azure-package-ci-job.yml@self
parameters:
abstractionsArtifactName: ${{ parameters.abstractionsArtifactName }}
abstractionsPackageVersion: ${{ parameters.abstractionsPackageVersion }}
@@ -111,7 +111,7 @@ stages:
# ------------------------------------------------------------------------
# Build and test on Windows
- - template: ../jobs/test-azure-package-ci-job.yml@self
+ - template: /eng/pipelines/jobs/test-azure-package-ci-job.yml@self
parameters:
abstractionsArtifactName: ${{ parameters.abstractionsArtifactName }}
abstractionsPackageVersion: ${{ parameters.abstractionsPackageVersion }}
@@ -128,7 +128,7 @@ stages:
# ------------------------------------------------------------------------
# Build and test on macOS.
- - template: ../jobs/test-azure-package-ci-job.yml
+ - template: /eng/pipelines/jobs/test-azure-package-ci-job.yml@self
parameters:
abstractionsArtifactName: ${{ parameters.abstractionsArtifactName }}
abstractionsPackageVersion: ${{ parameters.abstractionsPackageVersion }}
@@ -145,7 +145,7 @@ stages:
# ------------------------------------------------------------------------
# Create and publish the NuGet package.
- - template: ../jobs/pack-azure-package-ci-job.yml@self
+ - template: /eng/pipelines/jobs/pack-azure-package-ci-job.yml@self
parameters:
abstractionsArtifactName: ${{ parameters.abstractionsArtifactName }}
abstractionsPackageVersion: ${{ parameters.abstractionsPackageVersion }}
diff --git a/eng/pipelines/stages/stress-tests-ci-stage.yml b/eng/pipelines/stages/stress-tests-ci-stage.yml
index b7cea84b82..bc4bda6b56 100644
--- a/eng/pipelines/stages/stress-tests-ci-stage.yml
+++ b/eng/pipelines/stages/stress-tests-ci-stage.yml
@@ -132,7 +132,7 @@ stages:
# --------------------------------------------------------------------------
# Build and test on Linux.
- - template: ../jobs/stress-tests-ci-job.yml@self
+ - template: /eng/pipelines/jobs/stress-tests-ci-job.yml@self
parameters:
jobNameSuffix: linux
displayNamePrefix: Linux
@@ -150,7 +150,7 @@ stages:
# --------------------------------------------------------------------------
# Build and test on Windows
- - template: ../jobs/stress-tests-ci-job.yml
+ - template: /eng/pipelines/jobs/stress-tests-ci-job.yml@self
parameters:
jobNameSuffix: windows
displayNamePrefix: Win
@@ -173,7 +173,7 @@ stages:
# --------------------------------------------------------------------------
# Build and test on macOS.
- - template: ../jobs/stress-tests-ci-job.yml
+ - template: /eng/pipelines/jobs/stress-tests-ci-job.yml@self
parameters:
jobNameSuffix: macos
displayNamePrefix: macOS
From b3fd95bfb010d436b44085a670b252edba52b954 Mon Sep 17 00:00:00 2001
From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com>
Date: Fri, 2 Jan 2026 14:33:36 -0400
Subject: [PATCH 07/13] Fixed stale parameter names in PR pipelines.
---
eng/pipelines/sqlclient-pr-package-ref-pipeline.yml | 2 +-
eng/pipelines/sqlclient-pr-project-ref-pipeline.yml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/eng/pipelines/sqlclient-pr-package-ref-pipeline.yml b/eng/pipelines/sqlclient-pr-package-ref-pipeline.yml
index bc88cb5f38..da3e2f7b70 100644
--- a/eng/pipelines/sqlclient-pr-package-ref-pipeline.yml
+++ b/eng/pipelines/sqlclient-pr-package-ref-pipeline.yml
@@ -128,7 +128,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 2fc0cad210..1244476d67 100644
--- a/eng/pipelines/sqlclient-pr-project-ref-pipeline.yml
+++ b/eng/pipelines/sqlclient-pr-project-ref-pipeline.yml
@@ -128,7 +128,7 @@ extends:
parameters:
buildConfiguration: ${{ parameters.buildConfiguration }}
buildPlatforms: ${{ parameters.buildPlatforms }}
- buildType: Project
+ referenceType: Project
codeCovTargetFrameworks: ${{ parameters.codeCovTargetFrameworks }}
debug: ${{ parameters.debug }}
enableStressTests: ${{ parameters.enableStressTests }}
From 0e2a83e40aba0fa4f0a2d277486f4a9fa5cebfcc Mon Sep 17 00:00:00 2001
From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com>
Date: Fri, 2 Jan 2026 14:35:12 -0400
Subject: [PATCH 08/13] Fixed incorrect template paths.
---
.../common/templates/steps/ci-project-build-step.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/eng/pipelines/common/templates/steps/ci-project-build-step.yml b/eng/pipelines/common/templates/steps/ci-project-build-step.yml
index 7b93c0c1c2..4e6c8ac07d 100644
--- a/eng/pipelines/common/templates/steps/ci-project-build-step.yml
+++ b/eng/pipelines/common/templates/steps/ci-project-build-step.yml
@@ -53,12 +53,12 @@ parameters:
default: ''
steps:
-- template: /eng/pipelines/common/templates/ensure-dotnet-version.yml@self
+- template: /eng/pipelines/common/templates/steps/ensure-dotnet-version.yml@self
parameters:
packageType: 'sdk'
version: '10.0'
-- template: /eng/pipelines/common/templates/ensure-dotnet-version.yml@self
+- template: /eng/pipelines/common/templates/steps/ensure-dotnet-version.yml@self
parameters:
packageType: 'runtime'
version: '9.0'
From f07d849ee5f3480eea098b81b0251a7ee5ae4985 Mon Sep 17 00:00:00 2001
From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com>
Date: Fri, 2 Jan 2026 14:36:49 -0400
Subject: [PATCH 09/13] Fixed stale parameter names.
---
.../common/templates/steps/generate-nuget-package-step.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
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}}"'
From 4a0eb74c8fe85e3e70c9f5d2c184aca7a7140fd1 Mon Sep 17 00:00:00 2001
From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com>
Date: Fri, 2 Jan 2026 19:29:23 -0400
Subject: [PATCH 10/13] - Updated .NET SDK to 10 for Abstractions and Azure
packages. - Added .NET 10 as a target for the Abstractions and Azure tests.
---
.../jobs/pack-abstractions-package-ci-job.yml | 6 +++---
eng/pipelines/jobs/pack-azure-package-ci-job.yml | 8 ++++----
.../jobs/test-abstractions-package-ci-job.yml | 11 +++++++++--
eng/pipelines/jobs/test-azure-package-ci-job.yml | 11 +++++++++--
.../Abstractions/test/Abstractions.Test.csproj | 2 +-
.../Azure/test/Azure.Test.csproj | 2 +-
6 files changed, 27 insertions(+), 13 deletions(-)
diff --git a/eng/pipelines/jobs/pack-abstractions-package-ci-job.yml b/eng/pipelines/jobs/pack-abstractions-package-ci-job.yml
index 13bf3618fb..ce4e332012 100644
--- a/eng/pipelines/jobs/pack-abstractions-package-ci-job.yml
+++ b/eng/pipelines/jobs/pack-abstractions-package-ci-job.yml
@@ -102,12 +102,12 @@ jobs:
steps:
- # Install the .NET 9.0 SDK.
+ # Install the .NET 10.0 SDK.
- task: UseDotNet@2
- displayName: Install .NET 9.0 SDK
+ displayName: Install .NET 10.0 SDK
inputs:
packageType: sdk
- version: 9.x
+ version: 10.x
# We use the 'custom' command because the DotNetCoreCLI@2 task doesn't
# support all of our argument combinations for the different build steps.
diff --git a/eng/pipelines/jobs/pack-azure-package-ci-job.yml b/eng/pipelines/jobs/pack-azure-package-ci-job.yml
index 9cc33eadb1..9657916e62 100644
--- a/eng/pipelines/jobs/pack-azure-package-ci-job.yml
+++ b/eng/pipelines/jobs/pack-azure-package-ci-job.yml
@@ -141,13 +141,13 @@ jobs:
# 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
-
- # Install the .NET 9.0 SDK.
+
+ # Install the .NET 10.0 SDK.
- task: UseDotNet@2
- displayName: Install .NET 9.0 SDK
+ displayName: Install .NET 10.0 SDK
inputs:
packageType: sdk
- version: 9.x
+ version: 10.x
# We use the 'custom' command because the DotNetCoreCLI@2 task doesn't
# support all of our argument combinations for the different build steps.
diff --git a/eng/pipelines/jobs/test-abstractions-package-ci-job.yml b/eng/pipelines/jobs/test-abstractions-package-ci-job.yml
index a5e0f98978..f20793ca86 100644
--- a/eng/pipelines/jobs/test-abstractions-package-ci-job.yml
+++ b/eng/pipelines/jobs/test-abstractions-package-ci-job.yml
@@ -115,11 +115,18 @@ jobs:
steps:
- # Install the .NET 9.0 SDK.
+ # Install the .NET 10.0 SDK.
- task: UseDotNet@2
- displayName: Install .NET 9.0 SDK
+ displayName: Install .NET 10.0 SDK
inputs:
packageType: sdk
+ version: 10.x
+
+ # Install the .NET 9.0 runtime.
+ - task: UseDotNet@2
+ displayName: Install .NET 9.0 Runtime
+ inputs:
+ packageType: runtime
version: 9.x
# Install the .NET 8.0 runtime.
diff --git a/eng/pipelines/jobs/test-azure-package-ci-job.yml b/eng/pipelines/jobs/test-azure-package-ci-job.yml
index 8e1b8fc6db..1daffdd012 100644
--- a/eng/pipelines/jobs/test-azure-package-ci-job.yml
+++ b/eng/pipelines/jobs/test-azure-package-ci-job.yml
@@ -155,11 +155,18 @@ jobs:
- pwsh: cp $(Build.SourcesDirectory)/NuGet.config.local $(Build.SourcesDirectory)/NuGet.config
displayName: Use local NuGet.config
- # Install the .NET 9.0 SDK.
+ # Install the .NET 10.0 SDK.
- task: UseDotNet@2
- displayName: Install .NET 9.0 SDK
+ displayName: Install .NET 10.0 SDK
inputs:
packageType: sdk
+ version: 10.x
+
+ # Install the .NET 9.0 runtime.
+ - task: UseDotNet@2
+ displayName: Install .NET 9.0 Runtime
+ inputs:
+ packageType: runtime
version: 9.x
# Install the .NET 8.0 runtime.
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..52247e6130 100644
--- a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/Abstractions.Test.csproj
+++ b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/Abstractions.Test.csproj
@@ -1,7 +1,7 @@
- net462;net8.0;net9.0
+ net462;net8.0;net9.0;net10.0
enable
enable
false
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..a2397fe933 100644
--- a/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Azure.Test.csproj
+++ b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Azure.Test.csproj
@@ -1,7 +1,7 @@
- net462;net8.0;net9.0
+ net462;net8.0;net9.0;net10.0
enable
enable
false
From ea684f50e766117d77bbda316a8490e2c1086993 Mon Sep 17 00:00:00 2001
From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com>
Date: Mon, 5 Jan 2026 08:18:19 -0400
Subject: [PATCH 11/13] Fixed pipeline pool name for macOS test jobs.
---
eng/pipelines/dotnet-sqlclient-ci-core.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/eng/pipelines/dotnet-sqlclient-ci-core.yml b/eng/pipelines/dotnet-sqlclient-ci-core.yml
index 0db276944b..8c0b3b011e 100644
--- a/eng/pipelines/dotnet-sqlclient-ci-core.yml
+++ b/eng/pipelines/dotnet-sqlclient-ci-core.yml
@@ -522,7 +522,7 @@ stages:
# Self hosted SQL Server on Mac
mac_sql_22:
- pool: $(defaultHostedPoolName)
+ pool: Azure Pipelines
hostedPool: true
images:
MacOSLatest_Sql22: macos-latest
From beba4a4478cab6ea39694299f96c15a9f774427c Mon Sep 17 00:00:00 2001
From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com>
Date: Thu, 15 Jan 2026 08:53:43 -0400
Subject: [PATCH 12/13] Move ActiveDirectoryAuthenticationProvider Tests
(#3717)
---
.github/workflows/codeql.yml | 28 +-
Directory.Packages.props | 5 +
build.proj | 2 +-
eng/pipelines/akv-official-pipeline.yml | 2 +-
.../templates/steps/ensure-dotnet-version.yml | 7 +
.../steps/update-config-file-step.yml | 7 +-
eng/pipelines/dotnet-sqlclient-ci-core.yml | 35 +-
...qlclient-ci-package-reference-pipeline.yml | 15 +-
...qlclient-ci-project-reference-pipeline.yml | 15 +-
.../dotnet-sqlclient-signing-pipeline.yml | 2 +-
.../jobs/pack-abstractions-package-ci-job.yml | 35 +-
.../jobs/pack-azure-package-ci-job.yml | 43 +-
.../jobs/test-abstractions-package-ci-job.yml | 50 ++-
.../jobs/test-azure-package-ci-job.yml | 134 +++++-
.../sqlclient-pr-package-ref-pipeline.yml | 2 +-
.../sqlclient-pr-project-ref-pipeline.yml | 2 +-
.../build-abstractions-package-ci-stage.yml | 57 +--
.../stages/build-azure-package-ci-stage.yml | 139 +++++--
.../stages/stress-tests-ci-stage.yml | 53 ++-
.../steps/compound-build-akv-step.yml | 6 +
src/Directory.Build.props | 41 +-
.../Abstractions/src/Abstractions.csproj | 6 +
.../src/SqlAuthenticationProviderInternal.cs | 80 ++--
.../test/Abstractions.Test.csproj | 15 +
.../test/SqlAuthenticationProviderTest.cs | 13 +-
.../Azure/test/AADAuthenticationTests.cs | 33 ++
.../Azure/test/AADConnectionTest.cs | 377 +++++++++++++++++
.../Azure/test/Azure.Test.csproj | 40 ++
.../Azure/test/Config.cs | 275 ++++++++++++
.../Azure/test/DefaultAuthProviderTests.cs | 62 +++
.../Azure/test/StringExtensions.cs | 13 +
.../test/WorkloadIdentityFederationTests.cs | 65 +++
.../ref/Microsoft.Data.SqlClient.csproj | 12 +-
.../src/Microsoft.Data.SqlClient.csproj | 11 +-
.../netfx/ref/Microsoft.Data.SqlClient.csproj | 12 +-
.../netfx/src/Microsoft.Data.SqlClient.csproj | 13 +-
.../tests/Common/Common.csproj | 11 +-
.../FunctionalTests/AADAuthenticationTests.cs | 26 +-
...soft.Data.SqlClient.FunctionalTests.csproj | 17 +-
.../SqlAuthenticationProviderManagerTests.cs | 35 ++
.../SqlAuthenticationProviderTest.cs | 36 --
.../ManualTests/DataCommon/DataTestUtility.cs | 9 +
.../DataCommon/ManagedIdentityProvider.cs | 97 +++++
.../DataCommon/UsernamePasswordProvider.cs | 72 ++++
....Data.SqlClient.ManualTesting.Tests.csproj | 16 +-
.../AADFedAuthTokenRefreshTest.cs | 23 +-
.../ConnectivityTests/AADConnectionTest.cs | 390 +++++-------------
.../tests/StressTests/Directory.Build.props | 2 +-
.../StressTests/Directory.Packages.props | 30 +-
.../SqlClient.Stress.Framework.csproj | 1 +
.../Microsoft.Data.SqlClient.UnitTests.csproj | 14 +-
...rosoft.Data.SqlClient.TestUtilities.csproj | 9 +-
.../Utils.cs | 1 -
.../config.default.json | 3 +-
.../xunit.runner.json | 23 +-
.../Microsoft.SqlServer.Server.csproj | 2 +-
.../StringsHelper.cs | 4 +
tools/specs/Microsoft.Data.SqlClient.nuspec | 159 +++----
...DllsForNetFxProjectReferenceBuilds.targets | 51 +++
tools/targets/GenerateMdsPackage.targets | 8 +-
60 files changed, 2047 insertions(+), 699 deletions(-)
create mode 100644 src/Microsoft.Data.SqlClient.Extensions/Azure/test/AADAuthenticationTests.cs
create mode 100644 src/Microsoft.Data.SqlClient.Extensions/Azure/test/AADConnectionTest.cs
create mode 100644 src/Microsoft.Data.SqlClient.Extensions/Azure/test/Config.cs
create mode 100644 src/Microsoft.Data.SqlClient.Extensions/Azure/test/DefaultAuthProviderTests.cs
create mode 100644 src/Microsoft.Data.SqlClient.Extensions/Azure/test/StringExtensions.cs
create mode 100644 src/Microsoft.Data.SqlClient.Extensions/Azure/test/WorkloadIdentityFederationTests.cs
create mode 100644 src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlAuthenticationProviderManagerTests.cs
delete mode 100644 src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlAuthenticationProviderTest.cs
create mode 100644 src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/ManagedIdentityProvider.cs
create mode 100644 src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/UsernamePasswordProvider.cs
create mode 100644 tools/targets/CopySniDllsForNetFxProjectReferenceBuilds.targets
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index c5a76de2be..cddcff79de 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -13,9 +13,9 @@ name: "CodeQL Advanced"
on:
push:
- branches: [ "main" ]
+ branches: [ "main", "feat/*" ]
pull_request:
- branches: [ "main" ]
+ branches: [ "main", "feat/*" ]
schedule:
- cron: '33 23 * * 6'
@@ -60,9 +60,8 @@ jobs:
- name: Setup .NET Core SDK
uses: actions/setup-dotnet@v5.0.1
with:
- # TODO: Update this to .NET 10 once PR #3686 is complete.
# TODO: Replace this with global-json-file once PR #3797 is complete.
- dotnet-version: 9.x
+ dotnet-version: 10.x
dotnet-quality: ga
#global-json-file: global.json
@@ -88,9 +87,26 @@ jobs:
- name: Run manual build steps
if: matrix.build-mode == 'manual'
shell: bash
+ # TODO(https://sqlclientdrivers.visualstudio.com/ADO.Net/_workitems/edit/38655):
+ # For some reason, the StressTests projects fail to restore in this
+ # environment, so we skip them by explicitly building all of the other
+ # projects instead of the main solution file.
run: |
- mkdir packages
- dotnet build src/
+ dotnet build src/Microsoft.SqlServer.Server
+ dotnet build src/Microsoft.Data.SqlClient.Extensions/Abstractions/src
+ dotnet build src/Microsoft.Data.SqlClient.Extensions/Abstractions/test
+ dotnet build src/Microsoft.Data.SqlClient/netcore/ref
+ dotnet build src/Microsoft.Data.SqlClient/netcore/src
+ dotnet build src/Microsoft.Data.SqlClient/netfx/ref
+ dotnet build src/Microsoft.Data.SqlClient/netfx/src
+ dotnet build src/Microsoft.Data.SqlClient/src
+ dotnet build src/Microsoft.Data.SqlClient.Extensions/Azure/src
+ dotnet build src/Microsoft.Data.SqlClient.Extensions/Azure/test
+ dotnet build src/Microsoft.Data.SqlClient/add-ons/AzureKeyVaultProvider
+ dotnet build src/Microsoft.Data.SqlClient/tests/UnitTests
+ dotnet build src/Microsoft.Data.SqlClient/tests/FunctionalTests
+ dotnet build src/Microsoft.Data.SqlClient/tests/ManualTests
+ dotnet build src/Microsoft.Data.SqlClient/tests/PerformanceTests
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 39df423071..e86de72af2 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -83,6 +83,11 @@
+
+
+
+
+
diff --git a/build.proj b/build.proj
index d8343382e3..bb574ba093 100644
--- a/build.proj
+++ b/build.proj
@@ -521,7 +521,7 @@
-
+
diff --git a/eng/pipelines/akv-official-pipeline.yml b/eng/pipelines/akv-official-pipeline.yml
index 316ca9e6ea..f21ca656d3 100644
--- a/eng/pipelines/akv-official-pipeline.yml
+++ b/eng/pipelines/akv-official-pipeline.yml
@@ -49,7 +49,7 @@ resources:
ref: 'refs/heads/main'
extends:
- template: 'v2/OneBranch.${{ parameters.oneBranchType }}.CrossPlat.yml@templates'
+ template: /v2/OneBranch.${{ parameters.oneBranchType }}.CrossPlat.yml@templates
parameters:
featureFlags:
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/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 8c0b3b011e..a1e274a35e 100644
--- a/eng/pipelines/dotnet-sqlclient-ci-core.yml
+++ b/eng/pipelines/dotnet-sqlclient-ci-core.yml
@@ -103,6 +103,17 @@ parameters:
type: boolean
default: true
+- name: dotnetVerbosity
+ displayName: dotnet CLI Verbosity
+ type: string
+ default: normal
+ values:
+ - quiet
+ - minimal
+ - normal
+ - detailed
+ - diagnostic
+
variables:
- template: /eng/pipelines/libraries/ci-build-variables.yml@self
@@ -121,11 +132,11 @@ stages:
# under the given artifact name.
- template: /eng/pipelines/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
@@ -164,25 +175,31 @@ 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: diagnostic
# Run the stress tests, if desired.
- ${{ if eq(parameters.enableStressTests, true) }}:
- template: /eng/pipelines/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'
+ azurePackageVersion: $(azurePackageVersion)
+ dotnetVerbosity: ${{ parameters.dotnetVerbosity }}
# Run the MDS and AKV tests.
- template: /eng/pipelines/common/templates/stages/ci-run-tests-stage.yml@self
diff --git a/eng/pipelines/dotnet-sqlclient-ci-package-reference-pipeline.yml b/eng/pipelines/dotnet-sqlclient-ci-package-reference-pipeline.yml
index 88c3abfc01..32cd90abb7 100644
--- a/eng/pipelines/dotnet-sqlclient-ci-package-reference-pipeline.yml
+++ b/eng/pipelines/dotnet-sqlclient-ci-package-reference-pipeline.yml
@@ -168,14 +168,27 @@ 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
+ template: /eng/pipelines/dotnet-sqlclient-ci-core.yml@self
parameters:
buildConfiguration: ${{ parameters.buildConfiguration }}
buildPlatforms: ${{ parameters.buildPlatforms }}
referenceType: Package
codeCovTargetFrameworks: ${{ parameters.codeCovTargetFrameworks }}
debug: ${{ parameters.debug }}
+ dotnetVerbosity: ${{ parameters.dotnetVerbosity }}
enableStressTests: ${{ parameters.enableStressTests }}
targetFrameworks: ${{ parameters.targetFrameworks }}
targetFrameworksUnix: ${{ parameters.targetFrameworksUnix }}
diff --git a/eng/pipelines/dotnet-sqlclient-ci-project-reference-pipeline.yml b/eng/pipelines/dotnet-sqlclient-ci-project-reference-pipeline.yml
index 65104e6961..5c7dce804c 100644
--- a/eng/pipelines/dotnet-sqlclient-ci-project-reference-pipeline.yml
+++ b/eng/pipelines/dotnet-sqlclient-ci-project-reference-pipeline.yml
@@ -168,14 +168,27 @@ 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
+ template: /eng/pipelines/dotnet-sqlclient-ci-core.yml@self
parameters:
buildConfiguration: ${{ parameters.buildConfiguration }}
buildPlatforms: ${{ parameters.buildPlatforms }}
referenceType: Project
codeCovTargetFrameworks: ${{ parameters.codeCovTargetFrameworks }}
debug: ${{ parameters.debug }}
+ dotnetVerbosity: ${{ parameters.dotnetVerbosity }}
enableStressTests: ${{ parameters.enableStressTests }}
targetFrameworks: ${{ parameters.targetFrameworks }}
targetFrameworksUnix: ${{ parameters.targetFrameworksUnix }}
diff --git a/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml b/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml
index c759e8937b..9b45e6820d 100644
--- a/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml
+++ b/eng/pipelines/dotnet-sqlclient-signing-pipeline.yml
@@ -88,7 +88,7 @@ resources:
ref: refs/heads/main
extends:
- template: v2/OneBranch.${{parameters.oneBranchType }}.CrossPlat.yml@templates # https://aka.ms/obpipelines/templates
+ template: /v2/OneBranch.${{parameters.oneBranchType }}.CrossPlat.yml@templates # https://aka.ms/obpipelines/templates
parameters:
featureFlags:
# Suggested by MerlinBot (https://sqlclientdrivers.visualstudio.com/ADO.Net/_git/dotnet-sqlclient/pullrequest/4882)
diff --git a/eng/pipelines/jobs/pack-abstractions-package-ci-job.yml b/eng/pipelines/jobs/pack-abstractions-package-ci-job.yml
index ce4e332012..c928baa62d 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 10.0 SDK.
- task: UseDotNet@2
displayName: Install .NET 10.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 9657916e62..c2f6e408fe 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 f20793ca86..965c2d7d0c 100644
--- a/eng/pipelines/jobs/test-abstractions-package-ci-job.yml
+++ b/eng/pipelines/jobs/test-abstractions-package-ci-job.yml
@@ -7,8 +7,9 @@
# This job builds the Abstractions package and runs its tests for a set of .NET
# runtimes.
#
-# This template defines a job named 'test_abstractions_package_job_'
-# that can be depended on by downstream jobs.
+# This template defines a job named
+# 'test_abstractions_package_job_' that can be depended on by
+# downstream jobs.
parameters:
@@ -19,6 +20,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 +32,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 +61,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
@@ -77,19 +83,22 @@ jobs:
variables:
# The Abstractions test project file to use for all dotnet CLI commands.
+ #
+ # Building this project implicitly builds the Abstractions project.
- name: project
value: src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/Abstractions.Test.csproj
# 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 +124,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 10.0 SDK.
- task: UseDotNet@2
displayName: Install .NET 10.0 SDK
@@ -142,18 +156,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 1daffdd012..afba00dc03 100644
--- a/eng/pipelines/jobs/test-azure-package-ci-job.yml
+++ b/eng/pipelines/jobs/test-azure-package-ci-job.yml
@@ -7,8 +7,8 @@
# This job builds the Azure package and runs its tests for a set of .NET
# runtimes.
#
-# This template defines a job named 'test_azure_package_job_' that can
-# be depended on by downstream jobs.
+# This template defines a job named 'test_azure_package_job_'
+# that can be depended on by downstream jobs.
parameters:
@@ -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
@@ -101,15 +119,19 @@ jobs:
variables:
# The Azure test project file to use for all dotnet CLI commands.
+ #
+ # Building this project implicitly builds the Azure project.
- name: project
value: src/Microsoft.Data.SqlClient.Extensions/Azure/test/Azure.Test.csproj
# 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 +163,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 +178,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
@@ -179,32 +215,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: /eng/pipelines/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)
@@ -214,7 +305,14 @@ jobs:
- ${{ each runtime in parameters.netFrameworkRuntimes }}:
- task: DotNetCoreCLI@2
displayName: Test [${{ runtime }}]
+ env:
+ ${{ if startsWith(parameters.poolName, 'ADO-CI') }}:
+ ADO_POOL: 1
+ 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 da3e2f7b70..1d135cd328 100644
--- a/eng/pipelines/sqlclient-pr-package-ref-pipeline.yml
+++ b/eng/pipelines/sqlclient-pr-package-ref-pipeline.yml
@@ -124,7 +124,7 @@ parameters:
default: [false, true]
extends:
- template: dotnet-sqlclient-ci-core.yml@self
+ template: /eng/pipelines/dotnet-sqlclient-ci-core.yml@self
parameters:
buildConfiguration: ${{ parameters.buildConfiguration }}
buildPlatforms: ${{ parameters.buildPlatforms }}
diff --git a/eng/pipelines/sqlclient-pr-project-ref-pipeline.yml b/eng/pipelines/sqlclient-pr-project-ref-pipeline.yml
index 1244476d67..9da1ddca38 100644
--- a/eng/pipelines/sqlclient-pr-project-ref-pipeline.yml
+++ b/eng/pipelines/sqlclient-pr-project-ref-pipeline.yml
@@ -124,7 +124,7 @@ parameters:
default: [false, true]
extends:
- template: dotnet-sqlclient-ci-core.yml@self
+ template: /eng/pipelines/dotnet-sqlclient-ci-core.yml@self
parameters:
buildConfiguration: ${{ parameters.buildConfiguration }}
buildPlatforms: ${{ parameters.buildPlatforms }}
diff --git a/eng/pipelines/stages/build-abstractions-package-ci-stage.yml b/eng/pipelines/stages/build-abstractions-package-ci-stage.yml
index b626e425f2..1f28d01a8e 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: /eng/pipelines/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, net10.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: /eng/pipelines/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, net10.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: /eng/pipelines/jobs/test-abstractions-package-ci-job.yml@self
parameters:
- jobNameSuffix: macos
+ buildConfiguration: ${{ parameters.buildConfiguration }}
+ debug: ${{ parameters.debug }}
displayNamePrefix: macOS
+ dotnetVerbosity: ${{ parameters.dotnetVerbosity }}
+ jobNameSuffix: macos
+ netFrameworkRuntimes: []
+ netRuntimes: [net8.0, net9.0, net10.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: /eng/pipelines/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 ae5606806f..26b1d0d1eb 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
@@ -99,49 +136,98 @@ stages:
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
+ netRuntimes: [net8.0, net9.0, net10.0]
+ poolName: ${{ parameters.azurePoolName }}
referenceType: ${{ parameters.referenceType }}
- verbosity: ${{ parameters.verbosity }}
vmImage: ubuntu-latest
+ # Use our 1ES ADO pool for comprehensive testing.
+ - template: /eng/pipelines/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, net10.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: /eng/pipelines/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
+ netRuntimes: [net8.0, net9.0, net10.0]
+ poolName: ${{ parameters.azurePoolName }}
referenceType: ${{ parameters.referenceType }}
- verbosity: ${{ parameters.verbosity }}
vmImage: windows-latest
+ # Use our 1ES ADO pool for comprehensive testing.
+ - template: /eng/pipelines/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, net10.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: /eng/pipelines/jobs/test-azure-package-ci-job.yml@self
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
+ netRuntimes: [net8.0, net9.0, net10.0]
+ 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 +238,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 bc4bda6b56..e60f5a7a38 100644
--- a/eng/pipelines/stages/stress-tests-ci-stage.yml
+++ b/eng/pipelines/stages/stress-tests-ci-stage.yml
@@ -20,6 +20,14 @@
# depended on by downstream stages.
parameters:
+
+ # The Azure package version to stress test. This version must be available in
+ # one of the configured NuGet sources.
+ - name: azurePackageVersion
+ displayName: Azure Package Version
+ type: string
+ default: ''
+
# The type of build to produce (Debug or Release)
- name: buildConfiguration
displayName: Build Configuration
@@ -36,12 +44,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 +63,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,9 +104,10 @@ stages:
# dotnet CLI arguments common to all commands.
- name: commonArguments
value: >-
- --verbosity ${{parameters.verbosity}}
+ --verbosity ${{parameters.dotnetVerbosity}}
--artifacts-path $(dotnetArtifactsDir)
-p:MdsPackageVersion=${{parameters.mdsPackageVersion}}
+ -p:AzurePackageVersion=${{parameters.azurePackageVersion}}
# dotnet CLI arguments for build/run commands.
- name: buildArguments
diff --git a/eng/pipelines/steps/compound-build-akv-step.yml b/eng/pipelines/steps/compound-build-akv-step.yml
index c58d3febb9..2586f86aee 100644
--- a/eng/pipelines/steps/compound-build-akv-step.yml
+++ b/eng/pipelines/steps/compound-build-akv-step.yml
@@ -44,6 +44,12 @@ steps:
packageType: 'runtime'
version: '9.x'
+ - task: UseDotNet@2
+ displayName: 'Install .NET 8.x Runtime'
+ inputs:
+ packageType: 'runtime'
+ version: '8.x'
+
- task: MSBuild@1
displayName: 'Build.proj - BuildAkv'
inputs:
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 381a2d9817..f38c666907 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 @@
14
+
+
+
+ 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..53652e0d8b 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
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/SqlAuthenticationProviderInternal.cs b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/SqlAuthenticationProviderInternal.cs
index c14a758f76..309edf4998 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 compilation flags and the
+ // 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,17 @@ 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(https://sqlclientdrivers.visualstudio.com/ADO.Net/_workitems/edit/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 52247e6130..23e4ea451f 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..4c837332e5 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 compilation flags. 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
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..dda1afabef
--- /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 certainty 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 a2397fe933..592c4c3c56 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..1db51d1c87
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Config.cs
@@ -0,0 +1,275 @@
+// 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. Note that
+/// variable names are case-sensitive on non-Windows platforms.
+///
+/// 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 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();
+
+ internal static bool SupportsIntegratedSecurity() => IntegratedSecuritySupported;
+ internal static bool SupportsManagedIdentity() => ManagedIdentitySupported;
+ internal static bool SupportsSystemAssignedManagedIdentity() => SystemAssignedManagedIdentitySupported;
+
+ #endregion
+
+ #region Static Construction
+
+ ///
+ /// Static construction reads configuration settings from the config file
+ /// and environment variables.
+ ///
+ static Config()
+ {
+ // Read from the config.json file. If the TEST_MDS_CONFIG environment
+ // variable is set, use it. Otherwise, assume the config file is in the
+ // working directory and named config.json.
+ 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}");
+ throw;
+ }
+
+ // 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(
+ $" AdoPool: {AdoPool}");
+ 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
+
+ ///
+ /// Get a string property from a JSON element.
+ ///
+ /// The JSON element to read from.
+ /// The name of the property to read.
+ /// The string value of the property, or an empty string if not found or invalid.
+ 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;
+ }
+
+ ///
+ /// Get a boolean property from a JSON element.
+ ///
+ /// The JSON element to read from.
+ /// The name of the property to read.
+ /// The boolean value of the property, or false if not found or invalid.
+ 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;
+ }
+
+ ///
+ /// Get a boolean flag from an environment variable. The variable's value
+ /// is not examined; only its presence is checked.
+ ///
+ /// The name of the environment variable.
+ /// True if the environment variable is set; false otherwise.
+ private static bool GetEnvFlag(string name)
+ {
+ return Environment.GetEnvironmentVariable(name) is not null;
+ }
+
+ ///
+ /// Get a string value from an environment variable.
+ ///
+ /// The name of the environment variable.
+ /// The value of the environment variable, or an empty string if not set.
+ 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..50ae1c7c6e
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/DefaultAuthProviderTests.cs
@@ -0,0 +1,62 @@
+// 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 the SqlAuthenticationProviderManager
+ // class in the MDS package and the static GetProvider() and SetProvider()
+ // methods of the SqlAuthenticationProvider class in the Abstractions
+ // package. We're testing this here because this test project uses both of
+ // those packages, and this is a convenient place to put such a test.
+ [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..053e543ee1
--- /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 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/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.
+ //
+ // 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 aaccec8c11..d7821b8a35 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 8432a6427c..70cb79a8b0 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
@@ -1,9 +1,16 @@
+
Microsoft.Data.SqlClient
+ $(AssemblyName).NetCore
+
net8.0;net9.0
- Microsoft.Data.SqlClient is not supported on this platform.
+
$(OS)
+ Microsoft.Data.SqlClient is not supported on this platform.
false
netcoreapp
@@ -11,7 +18,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 29fbddc3b6..4cdfa44f73 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 61c0d4274a..e3e1309607 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
@@ -1,16 +1,23 @@
+
Microsoft.Data.SqlClient
+ $(AssemblyName).NetFx
+
net462
- $(OS)
+
{407890AC-9876-4FEF-A6F1-F36A876BAADE}
+ $(OS)
true
AnyCPU
$(BinFolder)$(Configuration).$(OutputPlatform)\
$(ObjFolder)$(Configuration).$(OutputPlatform)\
$(BinPath)$(AssemblyName)\netfx\
- $(OutputPath)\Microsoft.Data.SqlClient.xml
+ $(OutputPath)\$(AssemblyName).xml
$(ObjPath)$(AssemblyName)\netfx\
Framework $(BaseProduct)
false
@@ -898,7 +905,7 @@
Microsoft\Data\SqlClient\SqlUtil.cs
- Microsoft\Data\SqlClient\SSPI\NativeSspiContextProvider.windows..cs
+ Microsoft\Data\SqlClient\SSPI\NativeSspiContextProvider.windows.cs
Microsoft\Data\SqlClient\SSPI\NegotiateSspiContextProvider.cs
diff --git a/src/Microsoft.Data.SqlClient/tests/Common/Common.csproj b/src/Microsoft.Data.SqlClient/tests/Common/Common.csproj
index ec728d6a98..1e827794ed 100644
--- a/src/Microsoft.Data.SqlClient/tests/Common/Common.csproj
+++ b/src/Microsoft.Data.SqlClient/tests/Common/Common.csproj
@@ -40,10 +40,10 @@
-
+
PreserveNewest
xunit.runner.json
-
+
@@ -51,12 +51,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..bda156ce47 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,7 @@
-
+
@@ -144,27 +144,22 @@
-
-
+
+
+
-
- PreserveNewest
- %(Filename)%(Extension)
-
-
-
-
+
PreserveNewest
xunit.runner.json
-
+
diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlAuthenticationProviderManagerTests.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlAuthenticationProviderManagerTests.cs
new file mode 100644
index 0000000000..ecc9d8fa0f
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlAuthenticationProviderManagerTests.cs
@@ -0,0 +1,35 @@
+// 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 SqlAuthenticationProviderManagerTests
+ {
+ // The FunctionalTests project employs a .NET Framework app.config file
+ // that configures a dummy authentication provider for
+ // ActiveDirectoryInteractive authentication. Verify that this is
+ // respected.
+ [ConditionalFact(typeof(TestUtility), nameof(TestUtility.IsNetFramework))]
+ public void DefaultAuthenticationProviders_AppConfig()
+ {
+ // The provider for ActiveDirectoryInteractive should be our dummy
+ // provider.
+ Assert.IsType(
+ SqlAuthenticationProvider.GetProvider(
+ SqlAuthenticationMethod.ActiveDirectoryInteractive));
+
+ // There should be no provider for other methods. Spot-check a few.
+ Assert.Null(SqlAuthenticationProvider.GetProvider(
+ #pragma warning disable CS0618 // Type or member is obsolete
+ SqlAuthenticationMethod.ActiveDirectoryPassword));
+ #pragma warning restore CS0618 // Type or member is obsolete
+
+ Assert.Null(SqlAuthenticationProvider.GetProvider(
+ SqlAuthenticationMethod.ActiveDirectoryManagedIdentity));
+ }
+ }
+}
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 0bb9654efc..a62bb9787b 100644
--- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs
@@ -239,6 +239,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..5f9ca1eff0
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/UsernamePasswordProvider.cs
@@ -0,0 +1,72 @@
+// 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
+{
+ readonly 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;
+
+ using CancellationTokenSource cts = new();
+ cts.CancelAfter(parameters.ConnectionTimeout * 1000);
+
+ AuthenticationResult result =
+ #pragma warning disable CS0618 // Type or member is obsolete
+ await PublicClientApplicationBuilder.Create(_appClientId)
+ .WithAuthority(parameters.Authority)
+ .Build()
+ .AcquireTokenByUsernamePassword([scope], 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 username/password " +
+ $"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 a459a760c7..945270d65a 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 @@
+
+
@@ -330,14 +332,12 @@
-
-
@@ -370,11 +370,11 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
- PreserveNewest
- %(Filename)%(Extension)
-
+
+
+
+
PreserveNewest
@@ -388,9 +388,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 54900a4dd0..e99192732d 100644
--- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AADFedAuthTokenRefreshTest/AADFedAuthTokenRefreshTest.cs
@@ -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..fb26623a2e 100644
--- a/src/Microsoft.Data.SqlClient/tests/StressTests/Directory.Build.props
+++ b/src/Microsoft.Data.SqlClient/tests/StressTests/Directory.Build.props
@@ -6,7 +6,7 @@
- net462;net8.0;net9.0
+ net462;net8.0;net9.0;net10.0
+
+
+
true
true
@@ -21,18 +27,30 @@
-
-
-
-
-
- 6.1.0-preview2.25178.5
+
+
+ 6.1.3
+
+
+
+
+
+
+
+
+ $(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..38ccf678dd 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,18 @@
{
- "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json",
- "diagnosticMessages": true,
- "parallelizeAssembly": true,
- "shadowCopy": false,
- "printMaxEnumerableLength": 0,
- "printMaxStringLength": 0,
- "showLiveOutput": false
+ "_comment1": "xUnit v2 doesn't allow proper JSON comments, so we put our comments in keys that are ignored.",
+ "_comment2": "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..8f3915e18c 100644
--- a/src/Microsoft.SqlServer.Server/StringsHelper.cs
+++ b/src/Microsoft.SqlServer.Server/StringsHelper.cs
@@ -48,13 +48,17 @@ 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;
}
diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec
index 4792149ed2..1e8ea94d75 100644
--- a/tools/specs/Microsoft.Data.SqlClient.nuspec
+++ b/tools/specs/Microsoft.Data.SqlClient.nuspec
@@ -86,20 +86,20 @@
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
@@ -110,105 +110,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 @@
-
+
From 0617c8e8603068793994c8360858284fea880261 Mon Sep 17 00:00:00 2001
From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com>
Date: Fri, 16 Jan 2026 09:42:56 -0400
Subject: [PATCH 13/13] Azure Split - Use MDS Common Project (#3873)
---
.../jobs/pack-abstractions-package-ci-job.yml | 1 -
.../jobs/pack-azure-package-ci-job.yml | 1 -
.../jobs/test-abstractions-package-ci-job.yml | 1 -
.../jobs/test-azure-package-ci-job.yml | 50 ++++--
.../stages/build-azure-package-ci-stage.yml | 51 ++++--
src/Directory.Build.props | 39 -----
.../Abstractions/src/Abstractions.csproj | 8 +-
.../src/SqlAuthenticationProviderInternal.cs | 15 +-
.../test/Abstractions.Test.csproj | 10 +-
.../test/SqlAuthenticationProviderTest.cs | 13 +-
.../Azure/src/Azure.csproj | 6 +-
.../Azure/test/AADConnectionTest.cs | 25 ++-
.../Azure/test/Azure.Test.csproj | 16 +-
.../Azure/test/Config.cs | 3 -
.../Azure/test/StringExtensions.cs | 6 +-
.../ref/Microsoft.Data.SqlClient.csproj | 5 -
.../src/Microsoft.Data.SqlClient.csproj | 5 -
.../netfx/ref/Microsoft.Data.SqlClient.csproj | 5 -
.../netfx/src/Microsoft.Data.SqlClient.csproj | 5 -
.../src/Microsoft.Data.SqlClient.csproj | 45 ++++-
.../Data/Common/AdapterUtil.netfx.cs | 3 +
.../DbConnectionPoolIdentity.cs | 2 +-
.../SqlClient/Diagnostics/SqlClientMetrics.cs | 2 +
.../Microsoft/Data/SqlClient/SqlDependency.cs | 2 +
.../src/Microsoft/Data/SqlClient/SqlUtil.cs | 2 +
.../src/Microsoft/Data/SqlClient/TdsParser.cs | 2 +
tools/specs/Microsoft.Data.SqlClient.nuspec | 159 +++++++++---------
...DllsForNetFxProjectReferenceBuilds.targets | 11 +-
tools/targets/GenerateMdsPackage.targets | 7 +-
29 files changed, 243 insertions(+), 257 deletions(-)
diff --git a/eng/pipelines/jobs/pack-abstractions-package-ci-job.yml b/eng/pipelines/jobs/pack-abstractions-package-ci-job.yml
index c928baa62d..4fd0d8d73d 100644
--- a/eng/pipelines/jobs/pack-abstractions-package-ci-job.yml
+++ b/eng/pipelines/jobs/pack-abstractions-package-ci-job.yml
@@ -81,7 +81,6 @@ jobs:
value: >-
$(commonArguments)
--configuration ${{ parameters.buildConfiguration }}
- -p:ForceMdsAssemblyNameSuffix=true
-p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }}
# Explicitly unset the $PLATFORM environment variable that is set by the
diff --git a/eng/pipelines/jobs/pack-azure-package-ci-job.yml b/eng/pipelines/jobs/pack-azure-package-ci-job.yml
index c2f6e408fe..837fcc5409 100644
--- a/eng/pipelines/jobs/pack-azure-package-ci-job.yml
+++ b/eng/pipelines/jobs/pack-azure-package-ci-job.yml
@@ -100,7 +100,6 @@ jobs:
value: >-
--verbosity ${{ parameters.dotnetVerbosity }}
-p:ReferenceType=${{ parameters.referenceType }}
- -p:ForceMdsAssemblyNameSuffix=true
-p:AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }}
# dotnet CLI arguments for build/test/pack commands
diff --git a/eng/pipelines/jobs/test-abstractions-package-ci-job.yml b/eng/pipelines/jobs/test-abstractions-package-ci-job.yml
index 965c2d7d0c..a4fea75efa 100644
--- a/eng/pipelines/jobs/test-abstractions-package-ci-job.yml
+++ b/eng/pipelines/jobs/test-abstractions-package-ci-job.yml
@@ -98,7 +98,6 @@ jobs:
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.
diff --git a/eng/pipelines/jobs/test-azure-package-ci-job.yml b/eng/pipelines/jobs/test-azure-package-ci-job.yml
index afba00dc03..288030f3b8 100644
--- a/eng/pipelines/jobs/test-azure-package-ci-job.yml
+++ b/eng/pipelines/jobs/test-azure-package-ci-job.yml
@@ -97,6 +97,18 @@ parameters:
- Package
- Project
+ # Steps to run, if any, to configure a local SQL Server instance on the agent
+ # VM.
+ - name: sqlServerSetupSteps
+ type: stepList
+ default: []
+
+ # True if the VM image includes a local SQL Server that supports connections
+ # via integrated security.
+ - name: supportsIntegratedSecurity
+ type: boolean
+ default: false
+
# The pool VM image to use.
- name: vmImage
type: string
@@ -129,16 +141,24 @@ jobs:
value: >-
--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
+ # dotnet CLI arguments for build/test/pack commands.
- name: buildArguments
value: >-
$(commonArguments)
--configuration ${{ parameters.buildConfiguration }}
+ # dotnet CLI arguments for test commands.
+ #
+ # Filter out tests annotated with the ActiveIssue attribute, which for
+ # some reason uses the category 'failing' rather than 'ActiveIssue'.
+ #
+ - name: testArguments
+ value: >-
+ --filter "category != failing"
+
# Explicitly unset the $PLATFORM environment variable that is set by the
# 'ADO Build properties' Library in the ADO SqlClientDrivers public
# project. This is defined with a non-standard Platform of 'AnyCPU', and
@@ -232,19 +252,25 @@ jobs:
AzureKeyVaultTenantId: $(AzureKeyVaultTenantId)
# macOS doesn't support managed identities.
ManagedIdentitySupported: ${{ not(eq(parameters.vmImage, 'macos-latest')) }}
- SupportsIntegratedSecurity: ${{ eq(variables['SupportsIntegratedSecurity'], 'true') }}
+ SupportsIntegratedSecurity: ${{ parameters.supportsIntegratedSecurity }}
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.
+ # Avoid exposing secrets to pipeline jobs triggered via forks. This
+ # prevents external contributors from creating PRs and running
+ # pipelines that could expose these secrets.
+ #
+ # Note that this isn't a perfect restriction since internal
+ # contributors may want to use forks, but this would prevent them from
+ # running the full test suite. We don't have a better way to detect
+ # external parties though.
${{ if eq(variables['system.pullRequest.isFork'], 'False') }}:
AADPasswordConnectionString: $(AAD_PASSWORD_CONN_STR)
AADServicePrincipalSecret: $(AADServicePrincipalSecret)
+ # Perform any local SQL Server setup.
+ - ${{ parameters.sqlServerSetupSteps }}
+
# We use the 'custom' command because the DotNetCoreCLI@2 task doesn't
# support all of our argument combinations for the different build steps.
@@ -282,7 +308,7 @@ jobs:
# 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-') }}:
+ ${{ if ne(parameters.poolName, 'Azure Pipelines') }}:
ADO_POOL: 1
# When using connectedServiceName below, the DotNetCoreCLI task
# needs the system access token to be injected as this environment
@@ -299,14 +325,14 @@ jobs:
command: custom
custom: test
projects: $(project)
- arguments: $(buildArguments) --no-build -f ${{ runtime }}
+ arguments: $(buildArguments) $(testArguments) --no-build -f ${{ runtime }}
# Run the tests for each .NET Framework runtime.
- ${{ each runtime in parameters.netFrameworkRuntimes }}:
- task: DotNetCoreCLI@2
displayName: Test [${{ runtime }}]
env:
- ${{ if startsWith(parameters.poolName, 'ADO-CI') }}:
+ ${{ if ne(parameters.poolName, 'Azure Pipelines') }}:
ADO_POOL: 1
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
${{ if eq(parameters.debug, true) }}:
@@ -316,4 +342,4 @@ jobs:
command: custom
custom: test
projects: $(project)
- arguments: $(buildArguments) --no-build -f ${{ runtime }}
+ arguments: $(buildArguments) $(testArguments) --no-build -f ${{ runtime }}
diff --git a/eng/pipelines/stages/build-azure-package-ci-stage.yml b/eng/pipelines/stages/build-azure-package-ci-stage.yml
index 26b1d0d1eb..35504b2c85 100644
--- a/eng/pipelines/stages/build-azure-package-ci-stage.yml
+++ b/eng/pipelines/stages/build-azure-package-ci-stage.yml
@@ -46,8 +46,8 @@ parameters:
#
# Any pool specified here must contain images with the following names:
#
+ # - ADO-MMS22-SQL22
# - ADO-UB22-SQL22
- # - ADO-CI-Win11
#
default: $(ci_var_defaultPoolName)
@@ -139,7 +139,7 @@ stages:
debug: ${{ parameters.debug }}
displayNamePrefix: Linux
dotnetVerbosity: ${{ parameters.dotnetVerbosity }}
- jobNameSuffix: linux_basic
+ jobNameSuffix: linux
mdsArtifactName: MDS.Artifact
mdsPackageVersion: ${{ parameters.mdsPackageVersion }}
netFrameworkRuntimes: []
@@ -155,15 +155,25 @@ stages:
abstractionsPackageVersion: ${{ parameters.abstractionsPackageVersion }}
buildConfiguration: ${{ parameters.buildConfiguration }}
debug: ${{ parameters.debug }}
- displayNamePrefix: Linux
+ displayNamePrefix: Linux Integration
dotnetVerbosity: ${{ parameters.dotnetVerbosity }}
- jobNameSuffix: linux_comprehensive
+ jobNameSuffix: linux_integration
mdsArtifactName: MDS.Artifact
mdsPackageVersion: ${{ parameters.mdsPackageVersion }}
netFrameworkRuntimes: []
netRuntimes: [net8.0, net9.0, net10.0]
poolName: ${{ parameters.adoPoolName }}
referenceType: ${{ parameters.referenceType }}
+ # The image includes a SQL Server instance that we must configure.
+ sqlServerSetupSteps:
+ - template: /eng/pipelines/common/templates/steps/configure-sql-server-linux-step.yml@self
+ parameters:
+ # Override the template's default step condition. We always
+ # want this step to run.
+ condition: true
+ # Use the Azure DevOps Library variable "password" for the SA
+ # password.
+ password: $(password)
vmImage: ADO-UB22-SQL22
# ------------------------------------------------------------------------
@@ -178,7 +188,7 @@ stages:
debug: ${{ parameters.debug }}
displayNamePrefix: Win
dotnetVerbosity: ${{ parameters.dotnetVerbosity }}
- jobNameSuffix: windows_basic
+ jobNameSuffix: windows
mdsArtifactName: MDS.Artifact
mdsPackageVersion: ${{ parameters.mdsPackageVersion }}
netFrameworkRuntimes: [net462]
@@ -194,16 +204,33 @@ stages:
abstractionsPackageVersion: ${{ parameters.abstractionsPackageVersion }}
buildConfiguration: ${{ parameters.buildConfiguration }}
debug: ${{ parameters.debug }}
- displayNamePrefix: Win
+ displayNamePrefix: Win Integration
dotnetVerbosity: ${{ parameters.dotnetVerbosity }}
- jobNameSuffix: windows_comprehensive
+ jobNameSuffix: windows_integration
mdsArtifactName: MDS.Artifact
mdsPackageVersion: ${{ parameters.mdsPackageVersion }}
netFrameworkRuntimes: [net462]
netRuntimes: [net8.0, net9.0, net10.0]
poolName: ${{ parameters.adoPoolName }}
referenceType: ${{ parameters.referenceType }}
- vmImage: ADO-CI-Win11
+ # The image includes a SQL Server instance that we must configure.
+ sqlServerSetupSteps:
+ - template: /eng/pipelines/common/templates/steps/configure-sql-server-win-step.yml@self
+ # Use defaults for most parameters.
+ parameters:
+ # Override the template's default step condition. We always
+ # want this step to run.
+ condition: true
+ enableLocalDB: true
+ # These variables are from an Azure DevOps Library variable
+ # group.
+ fileStreamDirectory: $(FileStreamDirectory)
+ password: $(password)
+ sqlRootPath: $(SQL22RootPath)
+ # The ADO-MMS22-SQL22 image includes a local SQL Server that supports
+ # integrated security.
+ supportsIntegratedSecurity: true
+ vmImage: ADO-MMS22-SQL22
# ------------------------------------------------------------------------
# Build and test on macOS.
@@ -242,10 +269,10 @@ stages:
dependsOn:
# We depend on all of the test jobs to ensure the tests pass before
# producing the NuGet package.
- - 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_linux
+ - test_azure_package_job_linux_integration
+ - test_azure_package_job_windows
+ - test_azure_package_job_windows_integration
- test_azure_package_job_macos
dotnetVerbosity: ${{ parameters.dotnetVerbosity }}
referenceType: ${{ parameters.referenceType }}
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index f38c666907..7a3e277e2a 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -153,43 +153,4 @@
14
-
-
-
- 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 53652e0d8b..ddb74f9244 100644
--- a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Abstractions.csproj
+++ b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Abstractions.csproj
@@ -42,12 +42,6 @@
$(AbstractionsPackageVersion)
$(Artifacts)/doc/$(TargetFramework)/$(AssemblyName).xml
-
-
- $(DefineConstants);APPLY_MDS_ASSEMBLY_NAME_SUFFIX
@@ -86,7 +80,7 @@
dotnet.png
-
+
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/SqlAuthenticationProviderInternal.cs b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/SqlAuthenticationProviderInternal.cs
index 309edf4998..a1903e9279 100644
--- a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/SqlAuthenticationProviderInternal.cs
+++ b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/SqlAuthenticationProviderInternal.cs
@@ -32,20 +32,7 @@ private static class Internal
///
static Internal()
{
- // Choose the MDS assembly name based on compilation flags and the
- // 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
+ const string assemblyName = "Microsoft.Data.SqlClient";
// If the MDS package is present, load its
// SqlAuthenticationProviderManager class and get/set methods.
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 23e4ea451f..aeb752d648 100644
--- a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/Abstractions.Test.csproj
+++ b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/Abstractions.Test.csproj
@@ -9,14 +9,6 @@
Microsoft.Data.SqlClient.Extensions.Abstractions.Test
-
-
- $(DefineConstants);APPLY_MDS_ASSEMBLY_NAME_SUFFIX
-
-
@@ -33,7 +25,7 @@
-
+
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 4c837332e5..07e2b40078 100644
--- a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/SqlAuthenticationProviderTest.cs
+++ b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/SqlAuthenticationProviderTest.cs
@@ -8,23 +8,14 @@ namespace Microsoft.Data.SqlClient.Extensions.Abstractions.Test;
public class SqlAuthenticationProviderTest
{
- // Choose the MDS assembly name based on compilation flags. 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(assemblyName));
+ Assert.Throws(
+ () => Assembly.Load("Microsoft.Data.SqlClient"));
}
#region Tests
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Azure/src/Azure.csproj b/src/Microsoft.Data.SqlClient.Extensions/Azure/src/Azure.csproj
index 74dba18d1f..9ebb0806ac 100644
--- a/src/Microsoft.Data.SqlClient.Extensions/Azure/src/Azure.csproj
+++ b/src/Microsoft.Data.SqlClient.Extensions/Azure/src/Azure.csproj
@@ -80,7 +80,7 @@
dotnet.png
-
+
-
+
@@ -121,6 +121,6 @@
-->
$(AssemblyName)
-
+
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Azure/test/AADConnectionTest.cs b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/AADConnectionTest.cs
index dda1afabef..dd6f3bcdf0 100644
--- a/src/Microsoft.Data.SqlClient.Extensions/Azure/test/AADConnectionTest.cs
+++ b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/AADConnectionTest.cs
@@ -194,9 +194,22 @@ public static void ActiveDirectoryDefaultMustPass()
ConnectAndDisconnect(connStr);
}
+ // This test works on main in the existing jobs (like Win22_Sql22), but
+ // fails in the Azure project tests on a similar agent/image:
+ //
+ // Failed Microsoft.Data.SqlClient.Extensions.Azure.Test.AADConnectionTest.ADIntegratedUsingSSPI [59 ms]
+ // Error Message:
+ // Microsoft.Data.SqlClient.SqlException : Failed to authenticate the user NT Authority\Anonymous Logon in Active Directory (Authentication=ActiveDirectoryIntegrated).
+ // Error code 0xget_user_name_failed
+ // Failed to acquire access token for ActiveDirectoryIntegrated: Failed to get user name.
+ //
+ // ActiveIssue tests can be filtered out of test runs on the dotnet CLI
+ // using the filter "category != failing".
+ //
+ [ActiveIssue("https://sqlclientdrivers.visualstudio.com/ADO.Net/_workitems/edit/40107")]
[ConditionalFact(
typeof(Config),
- nameof(Config.HasIntegratedSecurityConnectionString),
+ nameof(Config.SupportsIntegratedSecurity),
nameof(Config.HasTcpConnectionString))]
public static void ADIntegratedUsingSSPI()
{
@@ -209,9 +222,9 @@ public static void ADIntegratedUsingSSPI()
[ConditionalFact(
typeof(Config),
- nameof(Config.HasPasswordConnectionString),
nameof(Config.SupportsManagedIdentity),
- nameof(Config.SupportsSystemAssignedManagedIdentity))]
+ nameof(Config.SupportsSystemAssignedManagedIdentity),
+ nameof(Config.HasPasswordConnectionString))]
public static void SystemAssigned_ManagedIdentityTest()
{
string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD" };
@@ -235,9 +248,9 @@ public static void UserAssigned_ManagedIdentityTest()
[ConditionalFact(
typeof(Config),
- nameof(Config.HasTcpConnectionString),
nameof(Config.SupportsManagedIdentity),
nameof(Config.SupportsSystemAssignedManagedIdentity),
+ nameof(Config.HasTcpConnectionString),
nameof(Config.IsAzureSqlServer))]
public static void Azure_SystemManagedIdentityTest()
{
@@ -255,10 +268,10 @@ public static void Azure_SystemManagedIdentityTest()
[ConditionalFact(
typeof(Config),
+ nameof(Config.OnAdoPool),
+ nameof(Config.SupportsManagedIdentity),
nameof(Config.HasTcpConnectionString),
nameof(Config.HasUserManagedIdentityClientId),
- nameof(Config.SupportsManagedIdentity),
- nameof(Config.SupportsSystemAssignedManagedIdentity),
nameof(Config.IsAzureSqlServer))]
public static void Azure_UserManagedIdentityTest()
{
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 592c4c3c56..bc4e5b14b1 100644
--- a/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Azure.Test.csproj
+++ b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Azure.Test.csproj
@@ -27,7 +27,7 @@
-
+
PreserveNewest
xunit.runner.json
@@ -41,21 +41,12 @@
tests into the artifacts. We don't refer to any of its assemblies.
-->
-
+
-
-
-
+
@@ -63,5 +54,4 @@
-
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Config.cs b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Config.cs
index 1db51d1c87..2a01a12384 100644
--- a/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Config.cs
+++ b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/Config.cs
@@ -4,7 +4,6 @@
using System.Runtime.InteropServices;
using System.Text.Json;
-
using Microsoft.Data.SqlClient.TestUtilities;
namespace Microsoft.Data.SqlClient.Extensions.Azure.Test;
@@ -56,8 +55,6 @@ internal static class Config
#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();
diff --git a/src/Microsoft.Data.SqlClient.Extensions/Azure/test/StringExtensions.cs b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/StringExtensions.cs
index acc7f10310..def794cc21 100644
--- a/src/Microsoft.Data.SqlClient.Extensions/Azure/test/StringExtensions.cs
+++ b/src/Microsoft.Data.SqlClient.Extensions/Azure/test/StringExtensions.cs
@@ -2,8 +2,10 @@
// 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.
+///
+/// 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)
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 d7821b8a35..8618e36fd7 100644
--- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.csproj
@@ -1,11 +1,6 @@
-
Microsoft.Data.SqlClient
- $(AssemblyName).NetCore
net8.0;net9.0;netstandard2.0
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 70cb79a8b0..b0465b4756 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
@@ -1,11 +1,6 @@
-
Microsoft.Data.SqlClient
- $(AssemblyName).NetCore
net8.0;net9.0
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 4cdfa44f73..e3e46f2349 100644
--- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj
@@ -1,11 +1,6 @@
-
Microsoft.Data.SqlClient
- $(AssemblyName).NetFx
net462
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 e3e1309607..1bcba9057c 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
@@ -1,11 +1,6 @@
-
Microsoft.Data.SqlClient
- $(AssemblyName).NetFx
net462
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj
index 9bc0137542..c17ce89279 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj
@@ -1,6 +1,7 @@
+ Microsoft.Data.SqlClient
Debug;Release;
true
@@ -15,11 +16,26 @@
$(SigningKeyPath)
+
+
+ net462;net8.0;net9.0
+
-
+
+ Windows_NT
$(OS)
@@ -34,13 +50,6 @@
$(DefineConstants);_WINDOWS
-
-
-
- net8.0;net9.0
- $(TargetFrameworks);net462
-
-
@@ -60,6 +69,26 @@
+
+
+
+ Microsoft.Data.SqlClient.Resources.Strings.resources
+ ResXFileCodeGenerator
+ Strings.Designer.cs
+ System
+
+
+
+
+ Microsoft.Data.SqlClient.Resources.%(Filename).resources
+
+
+
+
+ True
+ True
+ Strings.resx
+
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.netfx.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.netfx.cs
index c7427a6a8d..8b8d8b3c5b 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.netfx.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.netfx.cs
@@ -13,7 +13,10 @@
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
+
+#if _WINDOWS
using Interop.Windows.Kernel32;
+#endif
namespace Microsoft.Data.Common
{
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolIdentity.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolIdentity.cs
index 7eca7bd0f6..057433cf90 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolIdentity.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolIdentity.cs
@@ -80,7 +80,7 @@ internal static DbConnectionPoolIdentity GetCurrent()
#endif
}
- #if NETFRAMEWORK
+ #if NETFRAMEWORK && _WINDOWS
internal static WindowsIdentity GetCurrentWindowsIdentity() =>
WindowsIdentity.GetCurrent();
#endif
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.cs
index f6eb1c84c1..a813a03003 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Diagnostics/SqlClientMetrics.cs
@@ -8,7 +8,9 @@
#if NET
using System.Threading;
#else
+#if _WINDOWS
using Interop.Windows.Kernel32;
+#endif
using Microsoft.Data.Common;
using System.Diagnostics;
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependency.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependency.cs
index e237e0e3e7..9ce5c20342 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependency.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependency.cs
@@ -20,7 +20,9 @@
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security.Permissions;
+#if _WINDOWS
using Interop.Windows.Sni;
+#endif
using Microsoft.Data.SqlClient.LocalDb;
#endif
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUtil.cs
index 7c7d3e8c87..9054f88ab7 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUtil.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUtil.cs
@@ -21,7 +21,9 @@
#if NETFRAMEWORK
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
+#if _WINDOWS
using Interop.Windows.Kernel32;
+#endif
#else
using System.Net.Sockets;
#endif
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParser.cs
index 23063704ae..3f06a04ac6 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParser.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParser.cs
@@ -30,7 +30,9 @@
#if NETFRAMEWORK
using System.Runtime.CompilerServices;
+#if _WINDOWS
using Interop.Windows.Sni;
+#endif
using Microsoft.Data.SqlTypes;
#endif
diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec
index 1e8ea94d75..4792149ed2 100644
--- a/tools/specs/Microsoft.Data.SqlClient.nuspec
+++ b/tools/specs/Microsoft.Data.SqlClient.nuspec
@@ -86,20 +86,20 @@
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
@@ -110,110 +110,105 @@
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
diff --git a/tools/targets/CopySniDllsForNetFxProjectReferenceBuilds.targets b/tools/targets/CopySniDllsForNetFxProjectReferenceBuilds.targets
index e24a40cb01..47c45c41cd 100644
--- a/tools/targets/CopySniDllsForNetFxProjectReferenceBuilds.targets
+++ b/tools/targets/CopySniDllsForNetFxProjectReferenceBuilds.targets
@@ -34,15 +34,14 @@
Name="CopySniDllsForNetFx"
AfterTargets="CopyFilesToOutputDirectory"
Condition="'$(ReferenceType)' == 'Project' AND '$(TargetFramework)' == 'net462'">
-
-
-
- .NetFx
-
-
+
+
+
+
+
diff --git a/tools/targets/GenerateMdsPackage.targets b/tools/targets/GenerateMdsPackage.targets
index 1b3418860a..50f9b8fddd 100644
--- a/tools/targets/GenerateMdsPackage.targets
+++ b/tools/targets/GenerateMdsPackage.targets
@@ -1,11 +1,6 @@
-
- .NetFx
- .NetCore
-
-
@@ -14,6 +9,6 @@
-
+