diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000000..c9a6001477
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "dotnet.defaultSolution": "disable"
+}
\ No newline at end of file
diff --git a/Northwind-major8.png b/Northwind-major8.png
new file mode 100644
index 0000000000..8c1ad5e43f
Binary files /dev/null and b/Northwind-major8.png differ
diff --git a/README.md b/README.md
index e3f9266233..3ac4765303 100644
--- a/README.md
+++ b/README.md
@@ -14,6 +14,7 @@ Releases provide convenient downloads of sample databases and applications, elim
- [IoT Smart Grid sample v1.0](https://github.com/Microsoft/sql-server-samples/releases/tag/iot-smart-grid-v1.0) illustrates how SQL Server can be leveraged to ingest data from IoT devices and sensors, and how you can run analytics on that data.
To see the complete list of resources in this repository, navigate to [Releases](https://github.com/Microsoft/sql-server-samples/releases)
+(this now includes an entity diagram of Northwind database) to help visual learners.
## Working in GitHub
To contribute on GitHub, follow these steps:
diff --git a/samples/applications/aspnet-session-state/aspstate_sql2016_with_retry.sql b/samples/applications/aspnet-session-state/aspstate_sql2016_with_retry.sql
index 765775d555..1945710ecc 100644
--- a/samples/applications/aspnet-session-state/aspstate_sql2016_with_retry.sql
+++ b/samples/applications/aspnet-session-state/aspstate_sql2016_with_retry.sql
@@ -204,6 +204,9 @@ BEGIN
ELSE
BEGIN
PRINT 'Suffered an error for which Retry is inappropriate.';
+ IF XACT_STATE() = -1
+ ROLLBACK TRANSACTION;
+
THROW;
END
END CATCH
@@ -287,6 +290,9 @@ BEGIN
ELSE
BEGIN
PRINT 'Suffered an error for which Retry is inappropriate.';
+ IF XACT_STATE() = -1
+ ROLLBACK TRANSACTION;
+
THROW;
END
END CATCH
@@ -348,6 +354,9 @@ BEGIN
ELSE
BEGIN
PRINT 'Suffered an error for which Retry is inappropriate.';
+ IF XACT_STATE() = -1
+ ROLLBACK TRANSACTION;
+
THROW;
END
END CATCH
@@ -410,6 +419,9 @@ BEGIN
ELSE
BEGIN
PRINT 'Suffered an error for which Retry is inappropriate.';
+ IF XACT_STATE() = -1
+ ROLLBACK TRANSACTION;
+
THROW;
END
END CATCH
@@ -471,6 +483,9 @@ BEGIN
ELSE
BEGIN
PRINT 'Suffered an error for which Retry is inappropriate.';
+ IF XACT_STATE() = -1
+ ROLLBACK TRANSACTION;
+
THROW;
END
END CATCH
@@ -772,6 +787,9 @@ AS
ELSE
BEGIN
PRINT 'Suffered an error for which Retry is inappropriate.';
+ IF XACT_STATE() = -1
+ ROLLBACK TRANSACTION;
+
THROW;
END
END CATCH
@@ -838,6 +856,9 @@ AS
ELSE
BEGIN
PRINT 'Suffered an error for which Retry is inappropriate.';
+ IF XACT_STATE() = -1
+ ROLLBACK TRANSACTION;
+
THROW;
END
END CATCH
@@ -916,6 +937,9 @@ AS
ELSE
BEGIN
PRINT 'Suffered an error for which Retry is inappropriate.';
+ IF XACT_STATE() = -1
+ ROLLBACK TRANSACTION;
+
THROW;
END
END CATCH
@@ -996,6 +1020,9 @@ AS
ELSE
BEGIN
PRINT 'Suffered an error for which Retry is inappropriate.';
+ IF XACT_STATE() = -1
+ ROLLBACK TRANSACTION;
+
THROW;
END
END CATCH
@@ -1080,6 +1107,9 @@ AS
ELSE
BEGIN
PRINT 'Suffered an error for which Retry is inappropriate.';
+ IF XACT_STATE() = -1
+ ROLLBACK TRANSACTION;
+
THROW;
END
END CATCH
@@ -1149,6 +1179,9 @@ AS
ELSE
BEGIN
PRINT 'Suffered an error for which Retry is inappropriate.';
+ IF XACT_STATE() = -1
+ ROLLBACK TRANSACTION;
+
THROW;
END
END CATCH
@@ -1214,6 +1247,9 @@ AS
ELSE
BEGIN
PRINT 'Suffered an error for which Retry is inappropriate.';
+ IF XACT_STATE() = -1
+ ROLLBACK TRANSACTION;
+
THROW;
END
END CATCH
@@ -1255,6 +1291,9 @@ DECLARE @retry INT = 10;
ELSE
BEGIN
PRINT 'Suffered an error for which Retry is inappropriate.';
+ IF XACT_STATE() = -1
+ ROLLBACK TRANSACTION;
+
THROW;
END
END CATCH
@@ -1296,6 +1335,9 @@ DECLARE @retry INT = 10;
ELSE
BEGIN
PRINT 'Suffered an error for which Retry is inappropriate.';
+ IF XACT_STATE() = -1
+ ROLLBACK TRANSACTION;
+
THROW;
END
END CATCH
@@ -1335,6 +1377,9 @@ DECLARE @retry INT = 10;
ELSE
BEGIN
PRINT 'Suffered an error for which Retry is inappropriate.';
+ IF XACT_STATE() = -1
+ ROLLBACK TRANSACTION;
+
THROW;
END
END CATCH
@@ -1382,6 +1427,9 @@ DECLARE @retry INT = 10;
ELSE
BEGIN
PRINT 'Suffered an error for which Retry is inappropriate.';
+ IF XACT_STATE() = -1
+ ROLLBACK TRANSACTION;
+
THROW;
END
END CATCH
diff --git a/samples/databases/wide-world-importers/wwi-app/wwwroot/lib/webcomponentsjs/package-lock.json b/samples/databases/wide-world-importers/wwi-app/wwwroot/lib/webcomponentsjs/package-lock.json
index 008b8bcec6..122fa6d35c 100644
--- a/samples/databases/wide-world-importers/wwi-app/wwwroot/lib/webcomponentsjs/package-lock.json
+++ b/samples/databases/wide-world-importers/wwi-app/wwwroot/lib/webcomponentsjs/package-lock.json
@@ -4014,6 +4014,12 @@
"integrity": "sha1-ms1whRxtXf3ZPZKC5e35SgP/RrU=",
"dev": true
},
+ "cookie": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+ "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
+ "dev": true
+ },
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
@@ -4313,6 +4319,17 @@
"dev": true,
"optional": true
},
+ "define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dev": true,
+ "requires": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ }
+ },
"define-properties": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
@@ -4645,9 +4662,9 @@
}
},
"engine.io": {
- "version": "3.6.1",
- "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.6.1.tgz",
- "integrity": "sha512-dfs8EVg/i7QjFsXxn7cCRQ+Wai1G1TlEvHhdYEi80fxn5R1vZ2K661O6v/rezj1FP234SZ14r9CmJke99iYDGg==",
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.6.2.tgz",
+ "integrity": "sha512-C4JjGQZLY3kWlIDx0BQNKizbrfpb7NahxDztGdN5jrPK2ghmXiNDN+E/t0JzDeNRZxPVaszxEng42Pmj27X/0w==",
"dev": true,
"requires": {
"accepts": "~1.3.4",
@@ -4655,15 +4672,9 @@
"cookie": "~0.4.1",
"debug": "~4.1.0",
"engine.io-parser": "~2.2.0",
- "ws": "~7.4.2"
+ "ws": "~7.5.10"
},
"dependencies": {
- "cookie": {
- "version": "0.4.2",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
- "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
- "dev": true
- },
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
@@ -4678,13 +4689,19 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true
+ },
+ "ws": {
+ "version": "7.5.10",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
+ "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
+ "dev": true
}
}
},
"engine.io-client": {
- "version": "3.5.3",
- "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.3.tgz",
- "integrity": "sha512-qsgyc/CEhJ6cgMUwxRRtOndGVhIu5hpL5tR4umSpmX/MvkFoIxUTM7oFMDQumHNzlNLwSVy6qhstFPoWTf7dOw==",
+ "version": "3.5.4",
+ "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.4.tgz",
+ "integrity": "sha512-ydc8uuMMDxC5KCKNJN3zZKYJk2sgyTuTZQ7Aj1DJSsLKAcizA/PzWivw8fZMIjJVBo2CJOYzntv4FSjY/Lr//g==",
"dev": true,
"requires": {
"component-emitter": "~1.3.0",
@@ -4695,9 +4712,17 @@
"indexof": "0.0.1",
"parseqs": "0.0.6",
"parseuri": "0.0.6",
- "ws": "~7.4.2",
+ "ws": "~7.5.10",
"xmlhttprequest-ssl": "~1.6.2",
"yeast": "0.1.2"
+ },
+ "dependencies": {
+ "ws": {
+ "version": "7.5.10",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
+ "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
+ "dev": true
+ }
}
},
"engine.io-parser": {
@@ -4730,6 +4755,42 @@
}
}
},
+ "es-define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
+ "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
+ "dev": true,
+ "requires": {
+ "get-intrinsic": "^1.2.4"
+ },
+ "dependencies": {
+ "function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true
+ },
+ "get-intrinsic": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
+ "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
+ "dev": true,
+ "requires": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.0"
+ }
+ }
+ }
+ },
+ "es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true
+ },
"es5-ext": {
"version": "0.10.30",
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.30.tgz",
@@ -5015,37 +5076,37 @@
}
},
"express": {
- "version": "4.19.2",
- "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
- "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
+ "version": "4.21.1",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
+ "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
"dev": true,
"requires": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
- "body-parser": "1.20.2",
+ "body-parser": "1.20.3",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
- "cookie": "0.6.0",
+ "cookie": "0.7.1",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
- "encodeurl": "~1.0.2",
+ "encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
- "finalhandler": "1.2.0",
+ "finalhandler": "1.3.1",
"fresh": "0.5.2",
"http-errors": "2.0.0",
- "merge-descriptors": "1.0.1",
+ "merge-descriptors": "1.0.3",
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
- "path-to-regexp": "0.1.7",
+ "path-to-regexp": "0.1.10",
"proxy-addr": "~2.0.7",
- "qs": "6.11.0",
+ "qs": "6.13.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
- "send": "0.18.0",
- "serve-static": "1.15.0",
+ "send": "0.19.0",
+ "serve-static": "1.16.2",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"type-is": "~1.6.18",
@@ -5054,9 +5115,9 @@
},
"dependencies": {
"body-parser": {
- "version": "1.20.2",
- "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
- "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
+ "version": "1.20.3",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
+ "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
"dev": true,
"requires": {
"bytes": "3.1.2",
@@ -5067,7 +5128,7 @@
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
- "qs": "6.11.0",
+ "qs": "6.13.0",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
@@ -5081,10 +5142,23 @@
}
}
},
+ "call-bind": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
+ "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
+ "dev": true,
+ "requires": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.1"
+ }
+ },
"cookie": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
- "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
+ "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
"dev": true
},
"debug": {
@@ -5096,6 +5170,46 @@
"ms": "2.0.0"
}
},
+ "encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+ "dev": true
+ },
+ "finalhandler": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
+ "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "on-finished": "2.4.1",
+ "parseurl": "~1.3.3",
+ "statuses": "2.0.1",
+ "unpipe": "~1.0.0"
+ }
+ },
+ "function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true
+ },
+ "get-intrinsic": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
+ "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
+ "dev": true,
+ "requires": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.0"
+ }
+ },
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -5105,6 +5219,33 @@
"safer-buffer": ">= 2.1.2 < 3"
}
},
+ "merge-descriptors": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
+ "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
+ "dev": true
+ },
+ "object-inspect": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz",
+ "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==",
+ "dev": true
+ },
+ "path-to-regexp": {
+ "version": "0.1.10",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
+ "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==",
+ "dev": true
+ },
+ "qs": {
+ "version": "6.13.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
+ "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
+ "dev": true,
+ "requires": {
+ "side-channel": "^1.0.6"
+ }
+ },
"raw-body": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
@@ -5124,9 +5265,9 @@
"dev": true
},
"send": {
- "version": "0.18.0",
- "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
- "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
+ "version": "0.19.0",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
+ "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
"dev": true,
"requires": {
"debug": "2.6.9",
@@ -5144,6 +5285,12 @@
"statuses": "2.0.1"
},
"dependencies": {
+ "encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
+ "dev": true
+ },
"ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -5151,6 +5298,30 @@
"dev": true
}
}
+ },
+ "serve-static": {
+ "version": "1.16.2",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
+ "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
+ "dev": true,
+ "requires": {
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.3",
+ "send": "0.19.0"
+ }
+ },
+ "side-channel": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
+ "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.7",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.4",
+ "object-inspect": "^1.13.1"
+ }
}
}
},
@@ -5326,32 +5497,6 @@
"repeat-string": "1.6.1"
}
},
- "finalhandler": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
- "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
- "dev": true,
- "requires": {
- "debug": "2.6.9",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "on-finished": "2.4.1",
- "parseurl": "~1.3.3",
- "statuses": "2.0.1",
- "unpipe": "~1.0.0"
- },
- "dependencies": {
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- }
- }
- },
"find-port": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/find-port/-/find-port-1.0.1.tgz",
@@ -6219,6 +6364,15 @@
}
}
},
+ "gopd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+ "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+ "dev": true,
+ "requires": {
+ "get-intrinsic": "^1.1.3"
+ }
+ },
"got": {
"version": "6.7.1",
"resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz",
@@ -6687,6 +6841,12 @@
"get-intrinsic": "^1.1.1"
}
},
+ "has-proto": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
+ "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
+ "dev": true
+ },
"has-symbols": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
@@ -6753,6 +6913,23 @@
}
}
},
+ "hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.2"
+ },
+ "dependencies": {
+ "function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true
+ }
+ }
+ },
"he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
@@ -8374,12 +8551,6 @@
}
}
},
- "merge-descriptors": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
- "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==",
- "dev": true
- },
"merge-stream": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz",
@@ -9236,12 +9407,6 @@
"integrity": "sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==",
"dev": true
},
- "path-to-regexp": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
- "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==",
- "dev": true
- },
"path-type": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
@@ -11027,9 +11192,9 @@
}
},
"requirejs": {
- "version": "2.3.6",
- "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz",
- "integrity": "sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg==",
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.7.tgz",
+ "integrity": "sha512-DouTG8T1WanGok6Qjg2SXuCMzszOo0eHeH9hDZ5Y4x8Je+9JB38HdTLT4/VA8OaUhBa0JPVHJ0pyBkM1z+pDsw==",
"dev": true
},
"requires-port": {
@@ -11567,64 +11732,6 @@
}
}
},
- "serve-static": {
- "version": "1.15.0",
- "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
- "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
- "dev": true,
- "requires": {
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "parseurl": "~1.3.3",
- "send": "0.18.0"
- },
- "dependencies": {
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- },
- "dependencies": {
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
- "dev": true
- }
- }
- },
- "ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true
- },
- "send": {
- "version": "0.18.0",
- "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
- "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
- "dev": true,
- "requires": {
- "debug": "2.6.9",
- "depd": "2.0.0",
- "destroy": "1.2.0",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "etag": "~1.8.1",
- "fresh": "0.5.2",
- "http-errors": "2.0.0",
- "mime": "1.6.0",
- "ms": "2.1.3",
- "on-finished": "2.4.1",
- "range-parser": "~1.2.1",
- "statuses": "2.0.1"
- }
- }
- }
- },
"server-destroy": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz",
@@ -11643,6 +11750,50 @@
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
"dev": true
},
+ "set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dev": true,
+ "requires": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "dependencies": {
+ "function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true
+ },
+ "get-intrinsic": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
+ "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
+ "dev": true,
+ "requires": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.0"
+ }
+ },
+ "has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dev": true,
+ "requires": {
+ "es-define-property": "^1.0.0"
+ }
+ }
+ }
+ },
"set-value": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
@@ -11972,9 +12123,9 @@
"dev": true
},
"socket.io-parser": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.3.tgz",
- "integrity": "sha512-qOg87q1PMWWTeO01768Yh9ogn7chB9zkKtQnya41Y355S0UmpXgpcrFwAgjYJxu9BdKug5r5e9YtVSeWhKBUZg==",
+ "version": "3.3.4",
+ "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.4.tgz",
+ "integrity": "sha512-z/pFQB3x+EZldRRzORYW1vwVO8m/3ILkswtnpoeU6Ve3cbMWkmHEWDAVJn4QJtchiiFTo5j7UG2QvwxvaA9vow==",
"dev": true,
"requires": {
"component-emitter": "~1.3.0",
@@ -14373,12 +14524,6 @@
"signal-exit": "^3.0.2"
}
},
- "ws": {
- "version": "7.4.6",
- "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
- "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
- "dev": true
- },
"xdg-basedir": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz",
diff --git a/samples/databases/wide-world-importers/wwi-ssis/wwi-ssis/DailyETLMain.dtsx b/samples/databases/wide-world-importers/wwi-ssis/wwi-ssis/DailyETLMain.dtsx
index e3a97158cd..796d73cf74 100644
--- a/samples/databases/wide-world-importers/wwi-ssis/wwi-ssis/DailyETLMain.dtsx
+++ b/samples/databases/wide-world-importers/wwi-ssis/wwi-ssis/DailyETLMain.dtsx
@@ -6829,4 +6829,6255 @@ WITH RESULT SETS
WITH RESULT SETS
(
(
- [WWI Stock Item ID] int,
\ No newline at end of file
+ [WWI Stock Item ID] int,
+ [Stock Item] nvarchar(100),
+ Color nvarchar(20),
+ [Selling Package] nvarchar(50),
+ [Buying Package] nvarchar(50),
+ Brand nvarchar(50),
+ Size nvarchar(20),
+ [Lead Time Days] int,
+ [Quantity Per Outer] int,
+ [Is Chiller Stock] bit,
+ Barcode nvarchar(50),
+ [Tax Rate] decimal(18,3),
+ [Unit Price] decimal(18,2),
+ [Recommended Retail Price] decimal(18,2),
+ [Typical Weight Per Unit] decimal(18,3),
+ Photo varbinary(max),
+ [Valid From] datetime2(7),
+ [Valid To] datetime2(7)
+ )
+);
+
+ 1252
+ false
+ 2
+ "@LastCutoff:Input",{60BD9010-4784-49A6-8FF2-340D7AB3B70D};"@NewCutoff:Input",{B96D08A3-EC4E-45C1-8E78-526920753C6B};
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ [Integration].[StockItem_Staging]
+
+
+ 1252
+ false
+ 3
+ false
+ false
+ CHECK_CONSTRAINTS
+ 2147483647
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+
+
+ EXEC Integration.GetSupplierUpdates ?, ?
+WITH RESULT SETS
+(
+ (
+ [WWI Supplier ID] int,
+ Supplier nvarchar(100),
+ Category nvarchar(50),
+ [Primary Contact] nvarchar(50),
+ [Supplier Reference] nvarchar(20),
+ [Payment Days] int,
+ [Postal Code] nvarchar(10),
+ [Valid From] datetime2(7),
+ [Valid To] datetime2(7)
+ )
+);
+
+ 1252
+ false
+ 2
+ "@LastCutoff:Input",{60BD9010-4784-49A6-8FF2-340D7AB3B70D};"@NewCutoff:Input",{B96D08A3-EC4E-45C1-8E78-526920753C6B};
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ [Integration].[Supplier_Staging]
+
+
+ 1252
+ false
+ 3
+ false
+ false
+ CHECK_CONSTRAINTS
+ 2147483647
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+
+
+ EXEC Integration.GetTransactionUpdates ?, ?
+WITH RESULT SETS
+(
+ (
+ [Date Key] date,
+ [WWI Customer Transaction ID] int,
+ [WWI Supplier Transaction ID] int,
+ [WWI Invoice ID] int,
+ [WWI Purchase Order ID] int,
+ [Supplier Invoice Number] nvarchar(20),
+ [Total Excluding Tax] decimal(18,2),
+ [Tax Amount] decimal(18,2),
+ [Total Including Tax] decimal(18,2),
+ [Outstanding Balance] decimal(18,2),
+ [Is Finalized] bit,
+ [WWI Customer ID] int,
+ [WWI Bill To Customer ID] int,
+ [WWI Supplier ID] int,
+ [WWI Transaction Type ID] int,
+ [WWI Payment Method ID] int,
+ [Last Modified When] datetime2(7)
+ )
+);
+
+ 1252
+ false
+ 2
+ "@LastCutoff:Input",{60BD9010-4784-49A6-8FF2-340D7AB3B70D};"@NewCutoff:Input",{B96D08A3-EC4E-45C1-8E78-526920753C6B};
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ [Integration].[Transaction_Staging]
+
+
+ 1252
+ false
+ 3
+ false
+ false
+ CHECK_CONSTRAINTS
+ 2147483647
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+
+
+ EXEC Integration.GetTransactionTypeUpdates ?, ?
+WITH RESULT SETS
+(
+ (
+ [WWI Transaction Type ID] int,
+ [Transaction Type] nvarchar(50),
+ [Valid From] datetime2(7),
+ [Valid To] datetime2(7)
+ )
+);
+
+ 1252
+ false
+ 2
+ "@LastCutoff:Input",{60BD9010-4784-49A6-8FF2-340D7AB3B70D};"@NewCutoff:Input",{B96D08A3-EC4E-45C1-8E78-526920753C6B};
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ [Integration].[TransactionType_Staging]
+
+
+ 1252
+ false
+ 3
+ false
+ false
+ CHECK_CONSTRAINTS
+ 2147483647
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DataSourceViewID
+
+
+ TableInfoObjectType
+ Table
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DataSourceViewID
+
+
+ TableInfoObjectType
+ Table
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DataSourceViewID
+
+
+ TableInfoObjectType
+ Table
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DataSourceViewID
+
+
+ TableInfoObjectType
+ Table
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DataSourceViewID
+
+
+ TableInfoObjectType
+ Table
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DataSourceViewID
+
+
+ TableInfoObjectType
+ Table
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DataSourceViewID
+
+
+ TableInfoObjectType
+ Table
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DataSourceViewID
+
+
+ TableInfoObjectType
+ Table
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DataSourceViewID
+
+
+ TableInfoObjectType
+ Table
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DataSourceViewID
+
+
+ TableInfoObjectType
+ Table
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DataSourceViewID
+
+
+ TableInfoObjectType
+ Table
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DataSourceViewID
+
+
+ TableInfoObjectType
+ Table
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DataSourceViewID
+
+
+ TableInfoObjectType
+ Table
+
+
+
+]]>
+
\ No newline at end of file
diff --git a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/package-lock.json b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/package-lock.json
index 76a6dc205f..7d8d18149d 100644
--- a/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/package-lock.json
+++ b/samples/demos/azure-sql-edge-demos/Wind Turbine Demo/webappsrc/SqlDbEdgeDemoWeb/SqlDbEdgeDemo.Web/ClientApp/package-lock.json
@@ -2688,15 +2688,6 @@
"@types/json-schema": "*"
}
},
- "@types/eslint-scope": {
- "version": "3.7.4",
- "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz",
- "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==",
- "requires": {
- "@types/eslint": "*",
- "@types/estree": "*"
- }
- },
"@types/estree": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz",
@@ -3000,137 +2991,6 @@
"eslint-visitor-keys": "^3.3.0"
}
},
- "@webassemblyjs/ast": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
- "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==",
- "requires": {
- "@webassemblyjs/helper-numbers": "1.11.1",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.1"
- }
- },
- "@webassemblyjs/floating-point-hex-parser": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz",
- "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ=="
- },
- "@webassemblyjs/helper-api-error": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz",
- "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg=="
- },
- "@webassemblyjs/helper-buffer": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz",
- "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA=="
- },
- "@webassemblyjs/helper-numbers": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz",
- "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==",
- "requires": {
- "@webassemblyjs/floating-point-hex-parser": "1.11.1",
- "@webassemblyjs/helper-api-error": "1.11.1",
- "@xtuc/long": "4.2.2"
- }
- },
- "@webassemblyjs/helper-wasm-bytecode": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz",
- "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q=="
- },
- "@webassemblyjs/helper-wasm-section": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz",
- "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==",
- "requires": {
- "@webassemblyjs/ast": "1.11.1",
- "@webassemblyjs/helper-buffer": "1.11.1",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
- "@webassemblyjs/wasm-gen": "1.11.1"
- }
- },
- "@webassemblyjs/ieee754": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz",
- "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==",
- "requires": {
- "@xtuc/ieee754": "^1.2.0"
- }
- },
- "@webassemblyjs/leb128": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz",
- "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==",
- "requires": {
- "@xtuc/long": "4.2.2"
- }
- },
- "@webassemblyjs/utf8": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz",
- "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ=="
- },
- "@webassemblyjs/wasm-edit": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz",
- "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==",
- "requires": {
- "@webassemblyjs/ast": "1.11.1",
- "@webassemblyjs/helper-buffer": "1.11.1",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
- "@webassemblyjs/helper-wasm-section": "1.11.1",
- "@webassemblyjs/wasm-gen": "1.11.1",
- "@webassemblyjs/wasm-opt": "1.11.1",
- "@webassemblyjs/wasm-parser": "1.11.1",
- "@webassemblyjs/wast-printer": "1.11.1"
- }
- },
- "@webassemblyjs/wasm-gen": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz",
- "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==",
- "requires": {
- "@webassemblyjs/ast": "1.11.1",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
- "@webassemblyjs/ieee754": "1.11.1",
- "@webassemblyjs/leb128": "1.11.1",
- "@webassemblyjs/utf8": "1.11.1"
- }
- },
- "@webassemblyjs/wasm-opt": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz",
- "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==",
- "requires": {
- "@webassemblyjs/ast": "1.11.1",
- "@webassemblyjs/helper-buffer": "1.11.1",
- "@webassemblyjs/wasm-gen": "1.11.1",
- "@webassemblyjs/wasm-parser": "1.11.1"
- }
- },
- "@webassemblyjs/wasm-parser": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz",
- "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==",
- "requires": {
- "@webassemblyjs/ast": "1.11.1",
- "@webassemblyjs/helper-api-error": "1.11.1",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
- "@webassemblyjs/ieee754": "1.11.1",
- "@webassemblyjs/leb128": "1.11.1",
- "@webassemblyjs/utf8": "1.11.1"
- }
- },
- "@webassemblyjs/wast-printer": {
- "version": "1.11.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz",
- "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==",
- "requires": {
- "@webassemblyjs/ast": "1.11.1",
- "@xtuc/long": "4.2.2"
- }
- },
"@xtuc/ieee754": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
@@ -3196,10 +3056,10 @@
}
}
},
- "acorn-import-assertions": {
- "version": "1.8.0",
- "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz",
- "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw=="
+ "acorn-import-attributes": {
+ "version": "1.9.5",
+ "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz",
+ "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ=="
},
"acorn-jsx": {
"version": "5.3.2",
@@ -3852,6 +3712,103 @@
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
},
+ "body-parser": {
+ "version": "1.20.3",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
+ "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
+ "requires": {
+ "bytes": "3.1.2",
+ "content-type": "~1.0.5",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "http-errors": "2.0.0",
+ "iconv-lite": "0.4.24",
+ "on-finished": "2.4.1",
+ "qs": "6.13.0",
+ "raw-body": "2.5.2",
+ "type-is": "~1.6.18",
+ "unpipe": "1.0.0"
+ },
+ "dependencies": {
+ "bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="
+ },
+ "call-bind": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
+ "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
+ "requires": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.1"
+ }
+ },
+ "content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
+ },
+ "get-intrinsic": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
+ "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
+ "requires": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ },
+ "object-inspect": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz",
+ "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g=="
+ },
+ "qs": {
+ "version": "6.13.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
+ "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
+ "requires": {
+ "side-channel": "^1.0.6"
+ }
+ },
+ "side-channel": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
+ "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
+ "requires": {
+ "call-bind": "^1.0.7",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.4",
+ "object-inspect": "^1.13.1"
+ }
+ }
+ }
+ },
"bonjour-service": {
"version": "1.0.14",
"resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.14.tgz",
@@ -3885,11 +3842,21 @@
}
},
"braces": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
- "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"requires": {
- "fill-range": "^7.0.1"
+ "fill-range": "^7.1.1"
+ },
+ "dependencies": {
+ "fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "requires": {
+ "to-regex-range": "^5.0.1"
+ }
+ }
}
},
"browser-process-hrtime": {
@@ -4371,9 +4338,9 @@
}
},
"cross-spawn": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
- "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"requires": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
@@ -4705,6 +4672,16 @@
"execa": "^5.0.0"
}
},
+ "define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "requires": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ }
+ },
"define-lazy-prop": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
@@ -4937,9 +4914,9 @@
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
},
"ejs": {
- "version": "3.1.8",
- "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz",
- "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==",
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
+ "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
"requires": {
"jake": "^10.8.5"
}
@@ -4989,22 +4966,6 @@
}
}
},
- "enhanced-resolve": {
- "version": "5.10.0",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz",
- "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==",
- "requires": {
- "graceful-fs": "^4.2.4",
- "tapable": "^2.2.0"
- },
- "dependencies": {
- "graceful-fs": {
- "version": "4.2.10",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
- "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
- }
- }
- },
"entities": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
@@ -5072,10 +5033,37 @@
"resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz",
"integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA=="
},
- "es-module-lexer": {
- "version": "0.9.3",
- "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz",
- "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ=="
+ "es-define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
+ "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
+ "requires": {
+ "get-intrinsic": "^1.2.4"
+ },
+ "dependencies": {
+ "function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
+ },
+ "get-intrinsic": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
+ "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
+ "requires": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.0"
+ }
+ }
+ }
+ },
+ "es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="
},
"es-shim-unscopables": {
"version": "1.0.0",
@@ -5880,36 +5868,36 @@
}
},
"express": {
- "version": "4.19.2",
- "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
- "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
+ "version": "4.21.1",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
+ "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
"requires": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
- "body-parser": "1.20.2",
+ "body-parser": "1.20.3",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
- "cookie": "0.6.0",
+ "cookie": "0.7.1",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
- "encodeurl": "~1.0.2",
+ "encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
- "finalhandler": "1.2.0",
+ "finalhandler": "1.3.1",
"fresh": "0.5.2",
"http-errors": "2.0.0",
- "merge-descriptors": "1.0.1",
+ "merge-descriptors": "1.0.3",
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
- "path-to-regexp": "0.1.7",
+ "path-to-regexp": "0.1.10",
"proxy-addr": "~2.0.7",
- "qs": "6.11.0",
+ "qs": "6.13.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
- "send": "0.18.0",
- "serve-static": "1.15.0",
+ "send": "0.19.0",
+ "serve-static": "1.16.2",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"type-is": "~1.6.18",
@@ -5922,41 +5910,22 @@
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
},
- "body-parser": {
- "version": "1.20.2",
- "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
- "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
+ "call-bind": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
+ "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
"requires": {
- "bytes": "3.1.2",
- "content-type": "~1.0.5",
- "debug": "2.6.9",
- "depd": "2.0.0",
- "destroy": "1.2.0",
- "http-errors": "2.0.0",
- "iconv-lite": "0.4.24",
- "on-finished": "2.4.1",
- "qs": "6.11.0",
- "raw-body": "2.5.2",
- "type-is": "~1.6.18",
- "unpipe": "1.0.0"
- },
- "dependencies": {
- "content-type": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
- "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="
- }
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.1"
}
},
- "bytes": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
- "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="
- },
"cookie": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
- "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
+ "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w=="
},
"debug": {
"version": "2.6.9",
@@ -5966,26 +5935,80 @@
"ms": "2.0.0"
}
},
+ "encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="
+ },
+ "finalhandler": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
+ "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
+ "requires": {
+ "debug": "2.6.9",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "on-finished": "2.4.1",
+ "parseurl": "~1.3.3",
+ "statuses": "2.0.1",
+ "unpipe": "~1.0.0"
+ }
+ },
+ "function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
+ },
+ "get-intrinsic": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
+ "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
+ "requires": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.0"
+ }
+ },
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
- "raw-body": {
- "version": "2.5.2",
- "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
- "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
+ "object-inspect": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz",
+ "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g=="
+ },
+ "path-to-regexp": {
+ "version": "0.1.10",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
+ "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w=="
+ },
+ "qs": {
+ "version": "6.13.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
+ "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
"requires": {
- "bytes": "3.1.2",
- "http-errors": "2.0.0",
- "iconv-lite": "0.4.24",
- "unpipe": "1.0.0"
+ "side-channel": "^1.0.6"
}
},
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+ },
+ "side-channel": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
+ "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
+ "requires": {
+ "call-bind": "^1.0.7",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.4",
+ "object-inspect": "^1.13.1"
+ }
}
}
},
@@ -6098,43 +6121,6 @@
"resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz",
"integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ=="
},
- "fill-range": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
- "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
- "requires": {
- "to-regex-range": "^5.0.1"
- }
- },
- "finalhandler": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
- "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
- "requires": {
- "debug": "2.6.9",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "on-finished": "2.4.1",
- "parseurl": "~1.3.3",
- "statuses": "2.0.1",
- "unpipe": "~1.0.0"
- },
- "dependencies": {
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "requires": {
- "ms": "2.0.0"
- }
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
- }
- }
- },
"find-cache-dir": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
@@ -6564,6 +6550,14 @@
"minimatch": "~3.0.2"
}
},
+ "gopd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+ "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+ "requires": {
+ "get-intrinsic": "^1.1.3"
+ }
+ },
"graceful-fs": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
@@ -6628,6 +6622,11 @@
"get-intrinsic": "^1.1.1"
}
},
+ "has-proto": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
+ "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q=="
+ },
"has-symbols": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
@@ -6646,6 +6645,21 @@
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
},
+ "hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "requires": {
+ "function-bind": "^1.1.2"
+ },
+ "dependencies": {
+ "function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
+ }
+ }
+ },
"he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
@@ -6857,9 +6871,9 @@
}
},
"http-proxy-middleware": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz",
- "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==",
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz",
+ "integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==",
"requires": {
"@types/http-proxy": "^1.17.8",
"http-proxy": "^1.18.1",
@@ -9286,9 +9300,9 @@
}
},
"merge-descriptors": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
- "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
+ "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ=="
},
"merge-stream": {
"version": "2.0.0",
@@ -9306,11 +9320,11 @@
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="
},
"micromatch": {
- "version": "4.0.5",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
- "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"requires": {
- "braces": "^3.0.2",
+ "braces": "^3.0.3",
"picomatch": "^2.3.1"
}
},
@@ -10103,9 +10117,19 @@
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
},
"path-to-regexp": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
- "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.9.0.tgz",
+ "integrity": "sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g==",
+ "requires": {
+ "isarray": "0.0.1"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ=="
+ }
+ }
},
"path-type": {
"version": "4.0.0",
@@ -10998,14 +11022,6 @@
"resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
"integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw=="
},
- "qs": {
- "version": "6.11.0",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
- "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
- "requires": {
- "side-channel": "^1.0.4"
- }
- },
"querystringify": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
@@ -11042,6 +11058,24 @@
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
},
+ "raw-body": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
+ "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
+ "requires": {
+ "bytes": "3.1.2",
+ "http-errors": "2.0.0",
+ "iconv-lite": "0.4.24",
+ "unpipe": "1.0.0"
+ },
+ "dependencies": {
+ "bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="
+ }
+ }
+ },
"react": {
"version": "16.11.0",
"resolved": "https://registry.npmjs.org/react/-/react-16.11.0.tgz",
@@ -11307,14 +11341,6 @@
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
- },
- "path-to-regexp": {
- "version": "1.8.0",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
- "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
- "requires": {
- "isarray": "0.0.1"
- }
}
}
},
@@ -11827,9 +11853,9 @@
}
},
"rollup": {
- "version": "2.79.1",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz",
- "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==",
+ "version": "2.79.2",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz",
+ "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==",
"requires": {
"fsevents": "~2.3.2"
}
@@ -12061,9 +12087,9 @@
}
},
"send": {
- "version": "0.18.0",
- "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
- "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
+ "version": "0.19.0",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
+ "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
"requires": {
"debug": "2.6.9",
"depd": "2.0.0",
@@ -12171,14 +12197,21 @@
}
},
"serve-static": {
- "version": "1.15.0",
- "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
- "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
+ "version": "1.16.2",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
+ "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
"requires": {
- "encodeurl": "~1.0.2",
+ "encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
- "send": "0.18.0"
+ "send": "0.19.0"
+ },
+ "dependencies": {
+ "encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="
+ }
}
},
"set-blocking": {
@@ -12186,6 +12219,46 @@
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
},
+ "set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "requires": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "dependencies": {
+ "function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
+ },
+ "get-intrinsic": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
+ "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
+ "requires": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.0"
+ }
+ },
+ "has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "requires": {
+ "es-define-property": "^1.0.0"
+ }
+ }
+ }
+ },
"setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
@@ -13200,15 +13273,6 @@
"makeerror": "1.0.12"
}
},
- "watchpack": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
- "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==",
- "requires": {
- "glob-to-regexp": "^0.4.1",
- "graceful-fs": "^4.1.2"
- }
- },
"wbuf": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz",
@@ -13223,40 +13287,259 @@
"integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w=="
},
"webpack": {
- "version": "5.76.1",
- "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz",
- "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==",
- "requires": {
- "@types/eslint-scope": "^3.7.3",
- "@types/estree": "^0.0.51",
- "@webassemblyjs/ast": "1.11.1",
- "@webassemblyjs/wasm-edit": "1.11.1",
- "@webassemblyjs/wasm-parser": "1.11.1",
+ "version": "5.94.0",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz",
+ "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==",
+ "requires": {
+ "@types/estree": "^1.0.5",
+ "@webassemblyjs/ast": "^1.12.1",
+ "@webassemblyjs/wasm-edit": "^1.12.1",
+ "@webassemblyjs/wasm-parser": "^1.12.1",
"acorn": "^8.7.1",
- "acorn-import-assertions": "^1.7.6",
- "browserslist": "^4.14.5",
+ "acorn-import-attributes": "^1.9.5",
+ "browserslist": "^4.21.10",
"chrome-trace-event": "^1.0.2",
- "enhanced-resolve": "^5.10.0",
- "es-module-lexer": "^0.9.0",
+ "enhanced-resolve": "^5.17.1",
+ "es-module-lexer": "^1.2.1",
"eslint-scope": "5.1.1",
"events": "^3.2.0",
"glob-to-regexp": "^0.4.1",
- "graceful-fs": "^4.2.9",
+ "graceful-fs": "^4.2.11",
"json-parse-even-better-errors": "^2.3.1",
"loader-runner": "^4.2.0",
"mime-types": "^2.1.27",
"neo-async": "^2.6.2",
- "schema-utils": "^3.1.0",
+ "schema-utils": "^3.2.0",
"tapable": "^2.1.1",
- "terser-webpack-plugin": "^5.1.3",
- "watchpack": "^2.4.0",
+ "terser-webpack-plugin": "^5.3.10",
+ "watchpack": "^2.4.1",
"webpack-sources": "^3.2.3"
},
"dependencies": {
+ "@jridgewell/gen-mapping": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
+ "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
+ "requires": {
+ "@jridgewell/set-array": "^1.2.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "@jridgewell/set-array": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+ "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="
+ },
+ "@jridgewell/source-map": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
+ "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
+ "requires": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25"
+ }
+ },
+ "@jridgewell/trace-mapping": {
+ "version": "0.3.25",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+ "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+ "requires": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
"@types/estree": {
- "version": "0.0.51",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz",
- "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ=="
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
+ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
+ },
+ "@webassemblyjs/ast": {
+ "version": "1.12.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz",
+ "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==",
+ "requires": {
+ "@webassemblyjs/helper-numbers": "1.11.6",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.6"
+ }
+ },
+ "@webassemblyjs/floating-point-hex-parser": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz",
+ "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw=="
+ },
+ "@webassemblyjs/helper-api-error": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz",
+ "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q=="
+ },
+ "@webassemblyjs/helper-buffer": {
+ "version": "1.12.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz",
+ "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw=="
+ },
+ "@webassemblyjs/helper-numbers": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz",
+ "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==",
+ "requires": {
+ "@webassemblyjs/floating-point-hex-parser": "1.11.6",
+ "@webassemblyjs/helper-api-error": "1.11.6",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "@webassemblyjs/helper-wasm-bytecode": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz",
+ "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA=="
+ },
+ "@webassemblyjs/helper-wasm-section": {
+ "version": "1.12.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz",
+ "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==",
+ "requires": {
+ "@webassemblyjs/ast": "1.12.1",
+ "@webassemblyjs/helper-buffer": "1.12.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
+ "@webassemblyjs/wasm-gen": "1.12.1"
+ }
+ },
+ "@webassemblyjs/ieee754": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz",
+ "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==",
+ "requires": {
+ "@xtuc/ieee754": "^1.2.0"
+ }
+ },
+ "@webassemblyjs/leb128": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz",
+ "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==",
+ "requires": {
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "@webassemblyjs/utf8": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz",
+ "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA=="
+ },
+ "@webassemblyjs/wasm-edit": {
+ "version": "1.12.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz",
+ "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==",
+ "requires": {
+ "@webassemblyjs/ast": "1.12.1",
+ "@webassemblyjs/helper-buffer": "1.12.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
+ "@webassemblyjs/helper-wasm-section": "1.12.1",
+ "@webassemblyjs/wasm-gen": "1.12.1",
+ "@webassemblyjs/wasm-opt": "1.12.1",
+ "@webassemblyjs/wasm-parser": "1.12.1",
+ "@webassemblyjs/wast-printer": "1.12.1"
+ }
+ },
+ "@webassemblyjs/wasm-gen": {
+ "version": "1.12.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz",
+ "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==",
+ "requires": {
+ "@webassemblyjs/ast": "1.12.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
+ "@webassemblyjs/ieee754": "1.11.6",
+ "@webassemblyjs/leb128": "1.11.6",
+ "@webassemblyjs/utf8": "1.11.6"
+ }
+ },
+ "@webassemblyjs/wasm-opt": {
+ "version": "1.12.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz",
+ "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==",
+ "requires": {
+ "@webassemblyjs/ast": "1.12.1",
+ "@webassemblyjs/helper-buffer": "1.12.1",
+ "@webassemblyjs/wasm-gen": "1.12.1",
+ "@webassemblyjs/wasm-parser": "1.12.1"
+ }
+ },
+ "@webassemblyjs/wasm-parser": {
+ "version": "1.12.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz",
+ "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==",
+ "requires": {
+ "@webassemblyjs/ast": "1.12.1",
+ "@webassemblyjs/helper-api-error": "1.11.6",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
+ "@webassemblyjs/ieee754": "1.11.6",
+ "@webassemblyjs/leb128": "1.11.6",
+ "@webassemblyjs/utf8": "1.11.6"
+ }
+ },
+ "@webassemblyjs/wast-printer": {
+ "version": "1.12.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz",
+ "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==",
+ "requires": {
+ "@webassemblyjs/ast": "1.12.1",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "browserslist": {
+ "version": "4.23.3",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz",
+ "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==",
+ "requires": {
+ "caniuse-lite": "^1.0.30001646",
+ "electron-to-chromium": "^1.5.4",
+ "node-releases": "^2.0.18",
+ "update-browserslist-db": "^1.1.0"
+ }
+ },
+ "caniuse-lite": {
+ "version": "1.0.30001655",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001655.tgz",
+ "integrity": "sha512-jRGVy3iSGO5Uutn2owlb5gR6qsGngTw9ZTb4ali9f3glshcNmJ2noam4Mo9zia5P9Dk3jNNydy7vQjuE5dQmfg=="
+ },
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+ },
+ "electron-to-chromium": {
+ "version": "1.5.13",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz",
+ "integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q=="
+ },
+ "enhanced-resolve": {
+ "version": "5.17.1",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz",
+ "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==",
+ "requires": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.2.0"
+ }
+ },
+ "es-module-lexer": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz",
+ "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw=="
+ },
+ "escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="
},
"eslint-scope": {
"version": "5.1.1",
@@ -13272,10 +13555,15 @@
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
"integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="
},
+ "fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+ },
"graceful-fs": {
- "version": "4.2.10",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
- "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
},
"mime-db": {
"version": "1.52.0",
@@ -13289,6 +13577,82 @@
"requires": {
"mime-db": "1.52.0"
}
+ },
+ "node-releases": {
+ "version": "2.0.18",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz",
+ "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g=="
+ },
+ "picocolors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
+ "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew=="
+ },
+ "schema-utils": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
+ "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
+ "requires": {
+ "@types/json-schema": "^7.0.8",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
+ }
+ },
+ "serialize-javascript": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
+ "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
+ "requires": {
+ "randombytes": "^2.1.0"
+ }
+ },
+ "terser": {
+ "version": "5.31.6",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.6.tgz",
+ "integrity": "sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg==",
+ "requires": {
+ "@jridgewell/source-map": "^0.3.3",
+ "acorn": "^8.8.2",
+ "commander": "^2.20.0",
+ "source-map-support": "~0.5.20"
+ },
+ "dependencies": {
+ "acorn": {
+ "version": "8.12.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
+ "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg=="
+ }
+ }
+ },
+ "terser-webpack-plugin": {
+ "version": "5.3.10",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz",
+ "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==",
+ "requires": {
+ "@jridgewell/trace-mapping": "^0.3.20",
+ "jest-worker": "^27.4.5",
+ "schema-utils": "^3.1.1",
+ "serialize-javascript": "^6.0.1",
+ "terser": "^5.26.0"
+ }
+ },
+ "update-browserslist-db": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz",
+ "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==",
+ "requires": {
+ "escalade": "^3.1.2",
+ "picocolors": "^1.0.1"
+ }
+ },
+ "watchpack": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz",
+ "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==",
+ "requires": {
+ "glob-to-regexp": "^0.4.1",
+ "graceful-fs": "^4.1.2"
+ }
}
}
},
diff --git a/samples/extensibility/Install-MLModels.ps1 b/samples/extensibility/Install-MLModels.ps1
new file mode 100644
index 0000000000..a77ecbf1fb
--- /dev/null
+++ b/samples/extensibility/Install-MLModels.ps1
@@ -0,0 +1,565 @@
+#
+# Install-MLModels.ps1
+# Copyright (C) Microsoft. All rights reserved.
+#
+
+# See: https://learn.microsoft.com/sql/machine-learning/install/sql-pretrained-models-install
+
+# Verify script is being run with elevated privileges
+if (!([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator))
+{
+ Write-Error "This script must be run as Administrator"
+ Exit 1
+}
+
+class SqlInstance
+{
+ static $Languages = @{`
+ 'R' = @{`
+ 'ModelsPath' = 'library\MicrosoftML\mxLibs\x64';`
+ 'SharedKey' = 'sql_shared_mr';`
+ 'DllName' = 'MR';`
+ 'ConfigNode' = 'mrs' };`
+ 'Python' = @{`
+ 'ModelsPath' = 'Lib\site-packages\microsoftml\mxLibs';`
+ 'SharedKey' = 'sql_shared_mpy';`
+ 'DllName' = 'MPY';`
+ 'ConfigNode' = 'mpy' }}
+
+ [bool]$IsShared
+ [Version]$MlmVersion
+ [Version]$MlsVersion
+ [string]$Name
+ [string]$RootPath
+ [string]$RSetupPath
+ [string]$SqlVersion
+
+ #
+ # Returns SqlInstance or $null if an error occurred
+ #
+ [SqlInstance] static Create($isShared, $instanceName, $sqlVersion)
+ {
+ $sqlInstance = [SqlInstance]::new()
+
+ $sqlInstance.IsShared = $isShared
+ $sqlInstance.Name = $instanceName
+ $sqlInstance.SqlVersion = $sqlVersion
+ $sqlVersionName = ConvertSqlVersionToName( $sqlVersion )
+
+ if( [string]::IsNullOrEmpty($sqlVersionName) ){
+ Write-Warning "Sql name not found: instance name= $($instanceName), sql version=$($sqlVersion)"
+ return $null
+ }
+
+ if ($sqlInstance.IsShared)
+ {
+ $found = $false
+ foreach ($language in [SqlInstance]::Languages.Values)
+ {
+ $sharedRegPath = "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$sqlVersion\$($language.SharedKey)"
+ $sharedKey = Get-ItemProperty -Path $sharedRegPath -ErrorAction "Ignore"
+
+ if ($sharedKey -ne $null)
+ {
+ $sqlInstance.RootPath = (Get-Item($sharedKey.Path)).Parent.FullName
+ $sqlInstance.RSetupPath = "$($sqlInstance.RootPath)\Setup Bootstrap\{0}\x64\RSetup.exe" -f $sqlVersionName
+ $found = $true
+ break
+ }
+ }
+ if(!$found)
+ {
+ return $null
+ }
+ }
+ else
+ {
+ $setupRegPath = "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$instanceName\Setup"
+ $sqlInstance.RootPath = (Get-Item (Get-ItemProperty -Path $setupRegPath -ErrorAction "Ignore").SQLPath).Parent.FullName
+ $sqlInstance.RSetupPath = "{0}{1}\x64\RSetup.exe" -f`
+ (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$sqlVersion\Bootstrap" -ErrorAction "Ignore").BootstrapDir,`
+ $sqlVersionName
+ }
+
+ # Set MLS version
+ try
+ {
+ $bindingPath = Join-Path $sqlInstance.GetLanguageFolder("R") ".sqlbindr.ini"
+
+ if ((Test-Path $bindingPath) -and ((Get-Item -Force $bindingPath).Length -gt 0))
+ {
+ $sqlInstance.MlsVersion = Get-Content $bindingPath
+ $sqlInstance.RSetupPath = Join-Path (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\R Server').Path "Setup\rsetup.exe"
+ }
+ else
+ {
+ # Get MLS version from XML resource
+ foreach ($language in [SqlInstance]::Languages.Values)
+ {
+ $dllName = if ($sqlInstance.IsShared) {"S$($language.DllName)Dll"} else {"I$($language.DllName)Dll"}
+ $dllPath = if ($sqlInstance.IsShared) {"Shared\$dllName.dll"} else {"MSSQL\Binn\$dllName.dll"}
+ $dllPath = Join-Path $sqlInstance.RootPath $dllPath
+
+ if (Test-Path $dllPath)
+ {
+ $assembly = [System.Reflection.Assembly]::LoadFrom($dllPath)
+ $doc = New-Object System.Xml.XmlDocument
+ $doc.Load($assembly.GetManifestResourceStream("$dllName.$dllName.xml"))
+ $sqlInstance.MlsVersion = $doc.SelectSingleNode("/ConfigurationData/$($language.ConfigNode)").InnerText
+ break
+ }
+ }
+ }
+ }
+ catch
+ {
+ # Ignore exceptions
+ }
+
+ # check RSetup.exe exists
+ if (($sqlInstance.RSetupPath -eq $null) -or !(Test-Path -Path $sqlInstance.RSetupPath -PathType Leaf))
+ {
+ Write-Warning "RSetup.exe not found for instance name= $($instanceName), sql version=$($sqlVersion)"
+ return $null
+ }
+
+ # Map MLS version to MLM version
+ if ($sqlInstance.MlsVersion.ToString(3) -eq "9.2.0")
+ {
+ $sqlInstance.MlmVersion = "9.2.0.24"
+ }
+ elseif ($sqlInstance.MlsVersion.ToString(3) -eq "9.2.1"`
+ -or $sqlInstance.MlsVersion.ToString(3) -eq "9.3.0"`
+ -or $sqlInstance.MlsVersion.ToString(3) -eq "9.4.1"`
+ -or $sqlInstance.MlsVersion.ToString(3) -eq "9.4.7")
+ {
+ $sqlInstance.MlmVersion = $sqlInstance.MlsVersion.ToString(3)
+ }
+ else
+ {
+ $sqlInstance.MlmVersion = $sqlInstance.MlsVersion
+ }
+
+ return $sqlInstance
+ }
+
+
+ [string] GetLanguageFolder($language)
+ {
+ $languageFolder = $null
+
+ if ([SqlInstance]::Languages.ContainsKey($language))
+ {
+ if ($this.IsShared)
+ {
+ $languageFolder = Join-Path $this.RootPath "$($language)_SERVER".ToUpper()
+ }
+ else
+ {
+ $languageFolder = Join-Path $this.RootPath "$($language)_SERVICES".ToUpper()
+ }
+ }
+
+ return $languageFolder
+ }
+}
+
+#
+# Retursn sql version name or $null if name was not found
+#
+function ConvertSqlVersionToName($sqlVersion)
+{
+ $sqlVersionName = $null
+
+ foreach ($sqlKey in (Get-ChildItem -Path "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$sqlVersion" -ErrorAction "Ignore").PSChildName)
+ {
+ try
+ {
+ if ($sqlKey -imatch "^SQL(Server)?\d+$")
+ {
+ $sqlVersionName = $sqlKey
+ break
+ }
+ #example: SQL2019CTP3.0
+ if ($sqlKey -imatch "^SQL?\d{4}CTP\d+.\d+$")
+ {
+ $sqlVersionName = $sqlKey
+ break
+ }
+ }
+ catch
+ {
+ # Ignore exceptions
+ }
+ }
+
+ return $sqlVersionName
+}
+
+#
+# Returns number of instances found and hash table with successfully parsed instances
+#
+function GetSqlInstances()
+{
+ $sqlInstances = @{}
+
+ # Handle shared installs
+ foreach ($sqlVersion in (Get-ChildItem -Path 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server' -ErrorAction "Ignore").PSChildName)
+ {
+ try
+ {
+ $name = ConvertSqlVersionToName($sqlVersion)
+ if( ![string]::IsNullOrEmpty($name))
+ {
+ $sqlInstance = [SqlInstance]::Create($true, "SHARED_{0}" -f $name.ToUpper(), $sqlVersion)
+
+ if ( ($sqlInstance -ne $null) -and ($sqlInstance.MlmVersion -ne $null))
+ {
+ $sqlInstances[$sqlInstance.Name] = $sqlInstance
+ }
+ }
+ }
+ catch
+ {
+ # Ignore exceptions
+ }
+ }
+
+ # Handle per-instance installs
+ foreach ($sqlKey in (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL" -ErrorAction "Ignore").PSObject.Properties)
+ {
+ try
+ {
+ $versionRegPath = "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$($sqlKey.Value)\MSSQLServer\CurrentVersion"
+ $sqlVersion = (Get-ItemProperty -Path $versionRegPath -ErrorAction "Ignore").CurrentVersion -replace '^(\d+)\.(\d).*', '${1}0'
+
+ if( ![string]::IsNullOrEmpty($sqlVersion) )
+ {
+ $sqlInstance = [SqlInstance]::Create($false, $sqlKey.Value, $sqlVersion)
+
+ if (($sqlInstance -ne $null) -and ($sqlInstance.MlmVersion -ne $null))
+ {
+ $sqlInstances[$sqlKey.Name] = $sqlInstance
+ }
+ }
+ }
+ catch
+ {
+ # Ignore exceptions
+ }
+ }
+
+ return $sqlInstances
+}
+
+# Handle command-line arguments
+if ($Args -ne $null -and $Args.Length -gt 0)
+{
+ $sqlInstances = GetSqlInstances
+
+ if( $sqlInstances.Count -eq 0){
+ Write-Host "INFO: no SQL Server instances identified for model installation"
+ Exit 1
+ }
+
+ # Handle all instances
+ foreach ($arg in $Args)
+ {
+ if ($sqlInstances.ContainsKey($arg))
+ {
+ $sqlInstance = $sqlInstances[$arg]
+
+ Write-Host "INFO: processing instance $($sqlInstance.Name)"
+
+ foreach ($language in [SqlInstance]::Languages.Keys)
+ {
+ $languageFolder = $sqlInstance.GetLanguageFolder($language)
+ if (Test-Path $languageFolder)
+ {
+ $modelsPath = [SqlInstance]::Languages[$language].ModelsPath
+ $installDir = Join-Path $languageFolder $modelsPath
+ $rsetupArgs = "/component MLM /version $($sqlInstance.MlmVersion) /language 1033 /destdir `"$installDir`""
+
+ # Verify models version
+ Write-Host "`tVerifying $language models [$($sqlInstance.MlmVersion)]"
+ &$sqlInstance.RSetupPath "/checkurl $rsetupArgs" 2>&1 > $null
+ if ($lastexitcode -eq -1 -or $lastexitcode -eq 2)
+ {
+ Write-Warning "MLS version $($sqlInstance.MlmVersion) not supported"
+ break
+ }
+ elseif ($lastexitcode -gt 10000)
+ {
+ Write-Warning "Error downloading models [https://go.microsoft.com/fwlink/?LinkId=$lastexitcode&clcid=1033]"
+ break
+ }
+ elseif ($lastexitcode -ne 0)
+ {
+ Write-Warning "rsetup.exe exited with $lastexitcode"
+ break
+ }
+
+ # Download models, if not already cached
+ &$sqlInstance.RSetupPath "/checkcache $rsetupArgs" 2>&1 > $null
+ if ($lastexitcode -ne 0)
+ {
+ Write-Host "`tDownloading $language models [$Env:TEMP]"
+ &$sqlInstance.RSetupPath "/download $rsetupArgs" 2>&1 > $null
+
+ if ($lastexitcode -gt 10000)
+ {
+ Write-Warning "Error downloading models [https://go.microsoft.com/fwlink/?LinkId=$lastexitcode&clcid=1033]"
+ break
+ }
+ if ($lastexitcode -ne 0)
+ {
+ Write-Warning "rsetup.exe exited with $lastexitcode"
+ break
+ }
+ }
+
+ # Install models
+ Write-Host "`tInstalling $language models [$installDir]"
+ &$sqlInstance.RSetupPath "/install $rsetupArgs" 2>&1 > $null
+ if ($lastexitcode -eq 0)
+ {
+ Write-Host "SUCCESS installed $language for $($sqlInstance.Name)"
+ }
+ else
+ {
+ Write-Warning "rsetup.exe failed and exited with $lastexitcode for $language and $($sqlInstance.Name)"
+ break
+ }
+ }
+ }
+ }
+ else
+ {
+ Write-Warning "$($arg): invalid instance"
+ }
+ }
+}
+else
+{
+ Write-Host
+ Write-Host "USAGE: Install-MLModels.ps1 [ ...]"
+
+ $sqlInstances = GetSqlInstances
+
+ if( $sqlInstances.Count -eq 0)
+ {
+ Write-Host "NOTE: no SQL Server instances identified for model installation"
+ Exit 1
+ }
+
+ Write-Host
+ Write-Host "Available instances:"
+
+ # Display available instances
+ foreach ($instanceName in $sqlInstances.Keys | Sort-Object)
+ {
+ $sqlInstance = $sqlInstances[$instanceName]
+ Write-Host
+ Write-Host "`tInstance=$($instanceName)"
+ Write-Host "`tIsShared=$($sqlInstance.IsShared)"
+ Write-Host "`tMlmVersion=$($sqlInstance.MlmVersion)"
+ Write-Host "`tMlsVersion=$($sqlInstance.MlsVersion)"
+ Write-Host "`tRootPath=$($sqlInstance.RootPath)"
+ Write-Host "`tRSetupPath=$($sqlInstance.RSetupPath)"
+ Write-Host "`tSqlVersion=$($sqlInstance.SqlVersion)"
+ Write-Host "`tSqlVersionName=$(ConvertSqlVersionToName($sqlInstance.SqlVersion))"
+ }
+}
+
+# SIG # Begin signature block
+# MIIjigYJKoZIhvcNAQcCoIIjezCCI3cCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCOTihGgHRUMJIw
+# J5dFR3HXpebAuyRhtn6jmFBEPnttOKCCDYUwggYDMIID66ADAgECAhMzAAABUptA
+# n1BWmXWIAAAAAAFSMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
+# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
+# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
+# bmcgUENBIDIwMTEwHhcNMTkwNTAyMjEzNzQ2WhcNMjAwNTAyMjEzNzQ2WjB0MQsw
+# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
+# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
+# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+# AQCxp4nT9qfu9O10iJyewYXHlN+WEh79Noor9nhM6enUNbCbhX9vS+8c/3eIVazS
+# YnVBTqLzW7xWN1bCcItDbsEzKEE2BswSun7J9xCaLwcGHKFr+qWUlz7hh9RcmjYS
+# kOGNybOfrgj3sm0DStoK8ljwEyUVeRfMHx9E/7Ca/OEq2cXBT3L0fVnlEkfal310
+# EFCLDo2BrE35NGRjG+/nnZiqKqEh5lWNk33JV8/I0fIcUKrLEmUGrv0CgC7w2cjm
+# bBhBIJ+0KzSnSWingXol/3iUdBBy4QQNH767kYGunJeY08RjHMIgjJCdAoEM+2mX
+# v1phaV7j+M3dNzZ/cdsz3oDfAgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEE
+# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQU3f8Aw1sW72WcJ2bo/QSYGzVrRYcw
+# VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh
+# dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwMDEyKzQ1NDEzNjAfBgNVHSMEGDAW
+# gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v
+# d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw
+# MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov
+# L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx
+# XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB
+# AJTwROaHvogXgixWjyjvLfiRgqI2QK8GoG23eqAgNjX7V/WdUWBbs0aIC3k49cd0
+# zdq+JJImixcX6UOTpz2LZPFSh23l0/Mo35wG7JXUxgO0U+5drbQht5xoMl1n7/TQ
+# 4iKcmAYSAPxTq5lFnoV2+fAeljVA7O43szjs7LR09D0wFHwzZco/iE8Hlakl23ZT
+# 7FnB5AfU2hwfv87y3q3a5qFiugSykILpK0/vqnlEVB0KAdQVzYULQ/U4eFEjnis3
+# Js9UrAvtIhIs26445Rj3UP6U4GgOjgQonlRA+mDlsh78wFSGbASIvK+fkONUhvj8
+# B8ZHNn4TFfnct+a0ZueY4f6aRPxr8beNSUKn7QW/FQmn422bE7KfnqWncsH7vbNh
+# G929prVHPsaa7J22i9wyHj7m0oATXJ+YjfyoEAtd5/NyIYaE4Uu0j1EhuYUo5VaJ
+# JnMaTER0qX8+/YZRWrFN/heps41XNVjiAawpbAa0fUa3R9RNBjPiBnM0gvNPorM4
+# dsV2VJ8GluIQOrJlOvuCrOYDGirGnadOmQ21wPBoGFCWpK56PxzliKsy5NNmAXcE
+# x7Qb9vUjY1WlYtrdwOXTpxN4slzIht69BaZlLIjLVWwqIfuNrhHKNDM9K+v7vgrI
+# bf7l5/665g0gjQCDCN6Q5sxuttTAEKtJeS/pkpI+DbZ/MIIHejCCBWKgAwIBAgIK
+# YQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNV
+# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
+# c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlm
+# aWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEw
+# OTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
+# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYD
+# VQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG
+# 9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+la
+# UKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc
+# 6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4D
+# dato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+
+# lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nk
+# kDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6
+# A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmd
+# X4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL
+# 5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zd
+# sGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3
+# T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS
+# 4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRI
+# bmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAL
+# BgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBD
+# uRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jv
+# c29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf
+# MDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3
+# dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf
+# MDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEF
+# BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1h
+# cnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkA
+# YwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn
+# 8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7
+# v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0b
+# pdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/
+# KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvy
+# CInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBp
+# mLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJi
+# hsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYb
+# BL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbS
+# oqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sL
+# gOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtX
+# cVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCFVswghVXAgEBMIGVMH4x
+# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
+# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p
+# Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAAFSm0CfUFaZdYgAAAAA
+# AVIwDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw
+# HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIG3G
+# 7RvsqFwz7WY6U4HOPqdKK3BPgFwYBSdAqzbXoj3uMEIGCisGAQQBgjcCAQwxNDAy
+# oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
+# b20wDQYJKoZIhvcNAQEBBQAEggEAGyMo1lNn2g9GCbLVrzD8KtHpnboaxDtKsqdu
+# gIV6UjEl376n/D7+8zivlU+eSb1/H5hrorYzss2c0DJaZQG3wVjHzBuB/gmQHTig
+# RYTLqb9fYqmzIaPt0hPFyRDSRU0vbicr8b8bmIC2BSPZdkwAWWvXfgTtsoa+ay8N
+# G4DqDUJsGOkVLpPHQe0ixaH58OGRdFeQVN7XCDI5N+nM1V9LdxEy9HjCwNn8UBcE
+# NjDz9R+nUCqIfdwdcAiEuChu2iCIljSGI2WC9cnsE/fJSit2s7blZ9+tMwR9A9Ds
+# KsjD358pOzVBUQxFb9T+oaSBHxOeK7wdIK9LimSHzr75zkbxGKGCEuUwghLhBgor
+# BgEEAYI3AwMBMYIS0TCCEs0GCSqGSIb3DQEHAqCCEr4wghK6AgEDMQ8wDQYJYIZI
+# AWUDBAIBBQAwggFRBgsqhkiG9w0BCRABBKCCAUAEggE8MIIBOAIBAQYKKwYBBAGE
+# WQoDATAxMA0GCWCGSAFlAwQCAQUABCDVLWm2KCM2EapNQd+shZsDvjT3+xGk9ZHM
+# X7U16MZmPwIGXPEIIccdGBMyMDE5MDcxNjIzNTEwNy40MTFaMASAAgH0oIHQpIHN
+# MIHKMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1JlZG1vbmQx
+# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9z
+# b2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMg
+# VFNTIEVTTjpFMDQxLTRCRUUtRkE3RTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt
+# U3RhbXAgc2VydmljZaCCDjwwggTxMIID2aADAgECAhMzAAAA1p5lgY4NGKM7AAAA
+# AADWMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
+# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
+# cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw
+# MB4XDTE4MDgyMzIwMjY0OVoXDTE5MTEyMzIwMjY0OVowgcoxCzAJBgNVBAYTAlVT
+# MQswCQYDVQQIEwJXQTEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z
+# b2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVy
+# YXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOkUwNDEtNEJF
+# RS1GQTdFMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBzZXJ2aWNlMIIB
+# IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1c/AXoWbAgfvaGmvL/sJ4BCI
+# UKntNpiAOe5phUnhNPuCPMQDYTo+DAc1fctJaTqds4EBHniBT95Rm6fa1ejs3AsP
+# k7xUbBkjxmC1PAM7g3UEaPLDW8CZfmvx8A0UvkOUBuWkqvqjFrVawUX/hGbmJSC2
+# ljjsprizJmgSfjWnTHkdAj+yhiVeYcAehNOMsp1R6ctphRDwE+Kfj9sAarA3jxHV
+# OjG7WxQvIBXDgYSezQUEtX80U/HnMTLi+tD3W0CAvfX72jOfpQp9fUg8Jh8WiGzl
+# l02sNhicmM3gV4K4kPCaTNVjZyh8kcyi765Ofd3IJJUg3NDxoPIGADjWOjTbiQID
+# AQABo4IBGzCCARcwHQYDVR0OBBYEFGdUMJPgSTEafvZOFxynETg3j4j4MB8GA1Ud
+# IwQYMBaAFNVjOlyKMZDzQ3t8RhvFM2hahW1VMFYGA1UdHwRPME0wS6BJoEeGRWh0
+# dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1RpbVN0
+# YVBDQV8yMDEwLTA3LTAxLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKG
+# Pmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljVGltU3RhUENB
+# XzIwMTAtMDctMDEuY3J0MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUH
+# AwgwDQYJKoZIhvcNAQELBQADggEBAE/ShYCJm+Wlw+CRtcUt/ma3+rn0rliEPXG2
+# cBw3faMZjaJTfs3S9WPw8jVsYggVBu9exGJigWimWxY/9DR+p21tB+XwG8iTQfiw
+# ACWKiLGjDu4DfwhX54v/yCAVTsAi+bxFolbivR067fz0NHwuZAubqdt4a3K2+Ahn
+# 8csAJmFzkF+c8tLTgKFuit0zpnBIIZc591NOoK6vYSn+Be0rtgJhjeFeiZB2hpHo
+# CvDt62eyXLJs6JIleKNXEcGhNjpMlT6bG5+r2VXvx0EscTTaAVYwoE6L83VAgNAa
+# Eh/k+1zum8IbVNyes5I3/t4WPUWFx8R6Mjfi+2uWKdCGQI+8Jr8wggZxMIIEWaAD
+# AgECAgphCYEqAAAAAAACMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzET
+# MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV
+# TWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBD
+# ZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0xMDA3MDEyMTM2NTVaFw0yNTA3
+# MDEyMTQ2NTVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw
+# DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
+# JjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMIIBIjANBgkq
+# hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqR0NvHcRijog7PwTl/X6f2mUa3RUENWl
+# CgCChfvtfGhLLF/Fw+Vhwna3PmYrW/AVUycEMR9BGxqVHc4JE458YTBZsTBED/Fg
+# iIRUQwzXTbg4CLNC3ZOs1nMwVyaCo0UN0Or1R4HNvyRgMlhgRvJYR4YyhB50YWeR
+# X4FUsc+TTJLBxKZd0WETbijGGvmGgLvfYfxGwScdJGcSchohiq9LZIlQYrFd/Xcf
+# PfBXday9ikJNQFHRD5wGPmd/9WbAA5ZEfu/QS/1u5ZrKsajyeioKMfDaTgaRtogI
+# Neh4HLDpmc085y9Euqf03GS9pAHBIAmTeM38vMDJRF1eFpwBBU8iTQIDAQABo4IB
+# 5jCCAeIwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFNVjOlyKMZDzQ3t8RhvF
+# M2hahW1VMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAP
+# BgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fOmhjE
+# MFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kv
+# Y3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggrBgEF
+# BQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9w
+# a2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MIGgBgNVHSABAf8E
+# gZUwgZIwgY8GCSsGAQQBgjcuAzCBgTA9BggrBgEFBQcCARYxaHR0cDovL3d3dy5t
+# aWNyb3NvZnQuY29tL1BLSS9kb2NzL0NQUy9kZWZhdWx0Lmh0bTBABggrBgEFBQcC
+# AjA0HjIgHQBMAGUAZwBhAGwAXwBQAG8AbABpAGMAeQBfAFMAdABhAHQAZQBtAGUA
+# bgB0AC4gHTANBgkqhkiG9w0BAQsFAAOCAgEAB+aIUQ3ixuCYP4FxAz2do6Ehb7Pr
+# psz1Mb7PBeKp/vpXbRkws8LFZslq3/Xn8Hi9x6ieJeP5vO1rVFcIK1GCRBL7uVOM
+# zPRgEop2zEBAQZvcXBf/XPleFzWYJFZLdO9CEMivv3/Gf/I3fVo/HPKZeUqRUgCv
+# OA8X9S95gWXZqbVr5MfO9sp6AG9LMEQkIjzP7QOllo9ZKby2/QThcJ8ySif9Va8v
+# /rbljjO7Yl+a21dA6fHOmWaQjP9qYn/dxUoLkSbiOewZSnFjnXshbcOco6I8+n99
+# lmqQeKZt0uGc+R38ONiU9MalCpaGpL2eGq4EQoO4tYCbIjggtSXlZOz39L9+Y1kl
+# D3ouOVd2onGqBooPiRa6YacRy5rYDkeagMXQzafQ732D8OE7cQnfXXSYIghh2rBQ
+# Hm+98eEA3+cxB6STOvdlR3jo+KhIq/fecn5ha293qYHLpwmsObvsxsvYgrRyzR30
+# uIUBHoD7G4kqVDmyW9rIDVWZeodzOwjmmC3qjeAzLhIp9cAvVCch98isTtoouLGp
+# 25ayp0Kiyc8ZQU3ghvkqmqMRZjDTu3QyS99je/WZii8bxyGvWbWu3EQ8l1Bx16HS
+# xVXjad5XwdHeMMD9zOZN+w2/XU/pnR4ZOC+8z1gFLu8NoFA12u8JJxzVs341Hgi6
+# 2jbb01+P3nSISRKhggLOMIICNwIBATCB+KGB0KSBzTCByjELMAkGA1UEBhMCVVMx
+# CzAJBgNVBAgTAldBMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv
+# ZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh
+# dGlvbnMgTGltaXRlZDEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046RTA0MS00QkVF
+# LUZBN0UxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIHNlcnZpY2WiIwoB
+# ATAHBgUrDgMCGgMVAA9UX0q/L+thMJX0rozPt72QIBXRoIGDMIGApH4wfDELMAkG
+# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
+# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9z
+# b2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwDQYJKoZIhvcNAQEFBQACBQDg2NPEMCIY
+# DzIwMTkwNzE3MDY1MjUyWhgPMjAxOTA3MTgwNjUyNTJaMHcwPQYKKwYBBAGEWQoE
+# ATEvMC0wCgIFAODY08QCAQAwCgIBAAICIjQCAf8wBwIBAAICEYQwCgIFAODaJUQC
+# AQAwNgYKKwYBBAGEWQoEAjEoMCYwDAYKKwYBBAGEWQoDAqAKMAgCAQACAwehIKEK
+# MAgCAQACAwGGoDANBgkqhkiG9w0BAQUFAAOBgQBlNpEdpWGf/B83X8hAVPxRTgW4
+# 0prA9h6EG/xUWfp2c40h4vr0I2ee2oPbLNa9PTdTPO0deSBDcwM/J1lV7XV/5TgI
+# AxLLJgfSHdaIrZmiV38CZyd2vCQP6beG2TydK/aHkZpIApUQY54kQyB9X4QW5uPh
+# lis3qUZwulqZ4VaICjGCAw0wggMJAgEBMIGTMHwxCzAJBgNVBAYTAlVTMRMwEQYD
+# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
+# b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w
+# IFBDQSAyMDEwAhMzAAAA1p5lgY4NGKM7AAAAAADWMA0GCWCGSAFlAwQCAQUAoIIB
+# SjAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJKoZIhvcNAQkEMSIEIP5z
+# azPb5aBR/KZQlipF+nKTJNp041jk7Yld+TnbBe99MIH6BgsqhkiG9w0BCRACLzGB
+# 6jCB5zCB5DCBvQQgDKcXGy85Pqmxmt5kRTcsOqGjceOxduVb/tGYJy6USM4wgZgw
+# gYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
+# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD
+# VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAANaeZYGODRij
+# OwAAAAAA1jAiBCBsua44ho7AmDyDQPtF5fHr6CZ0JRmATzJfI/y++JB4GzANBgkq
+# hkiG9w0BAQsFAASCAQCBHgNjNO2+pFexw7gNQkDtrTM/I0APnvHsVOXLFSVTSj5P
+# HMrq+jR+IBnm8WCP9rRij/XZ2d/yM//JAzajALNk0mX2G6xuJx56xX8r2Q6Xvns3
+# a2v6a31u2bA+jaBWUK5089uIoLARpDOIlibuY0HSWavr0TntPL4c9oj5ZrWGVHDf
+# m36zM9J4wobjmzCjQBTBCtik3TZ1QPIpGErjEV6MyRP3cEDFBg6TLK11IPVpE0YE
+# N/aAHifwolmj9n5vqnMiWgKhiN7C7uIbOo0ytbeYu2dzRzIQvwuyWsgy5RJYkpkp
+# UUHb1YVGyl3yhOqan9rU68Qiza/tgULbBhXYn68L
+# SIG # End signature block
\ No newline at end of file
diff --git a/samples/extensibility/Microsoft Python packages/9.2.1/Install-PyForMLS.ps1 b/samples/extensibility/Microsoft Python packages/9.2.1/Install-PyForMLS.ps1
new file mode 100644
index 0000000000..5738ed5d40
--- /dev/null
+++ b/samples/extensibility/Microsoft Python packages/9.2.1/Install-PyForMLS.ps1
@@ -0,0 +1,128 @@
+#
+# Install-PyForMLS.ps1
+# Copyright (C) Microsoft. All rights reserved.
+#
+
+# See: https://learn.microsoft.com/sql/machine-learning/python/setup-python-client-tools-sql
+
+# Command-line parameters
+param
+(
+ [string]$InstallFolder = "$Env:ProgramFiles\Microsoft\PyForMLS",
+ [string]$CacheFolder = $Env:Temp,
+ [string]$JupyterShortcut = "$Env:ProgramData\Microsoft\Windows\Start Menu\Jupyter Notebook for Microsoft Machine Learning Server.lnk"
+)
+
+# Verify script is being run with elevated privileges
+if (!([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator))
+{
+ Write-Error "This script must be run as Administrator"
+ Exit 1
+}
+
+# Product version to install
+$productVersion = '9.2.1.0'
+
+# Default language to install (en-us)
+$productLcid = '1033'
+
+# Component names and their corresponding fwlink IDs
+$components = @{ 'SPO' = '852723'; 'SPS' = '852726'; }
+
+# Helper function which outputs the specified message to the console and prepends the current date and time
+function WriteMessage($message)
+{
+ Write-Host ("[{0}] {1}" -f (Get-Date -format u), $message)
+}
+
+# Helper function which generates a fwlink for the specified component
+function GenerateFwlink($componentName)
+{
+ return 'https://go.microsoft.com/fwlink/?LinkId={0}&clcid={1}' -f $components[$componentName], $productLcid
+}
+
+# Helper function which generates the absolute CAB path of the specified component
+function GenerateCabPath($componentName)
+{
+ return "{0}\{1}_{2}_1033.cab" -f $cacheFolder, $componentName, $productVersion
+}
+
+# Helper function which returns true if the specified CAB file is found in the cache
+function IsCachedCabValid($cabFile, $cabUrl)
+{
+ $isValid = $false
+
+ # Does CAB file exist?
+ if (Test-Path $cabFile)
+ {
+ # Retrieve headers from CAB URL
+ $response = Invoke-WebRequest -Method Head -Uri $cabUrl
+
+ # Compare last modified date of cached file against the remote file
+ if ([datetime](Get-ItemProperty -Path $cabFile).LastWriteTime -ge $response.Headers['Last-Modified'])
+ {
+ WriteMessage "Cached $cabFile is up-to-date"
+ $isValid = $true
+ }
+ else
+ {
+ WriteMessage "Cached $cabFile is expired"
+ $isValid = $false
+ }
+ }
+ else
+ {
+ WriteMessage "$cabFile not found in cache"
+ $isValid = $false
+ }
+
+ return $isValid
+}
+
+WriteMessage "Starting installation"
+
+# Create install folder, if necessary
+if (-not (Test-Path $installFolder))
+{
+ WriteMessage "Creating install folder $installFolder"
+ New-Item -ItemType directory -Path $installFolder > $null
+}
+
+# Download the CAB file for each component
+foreach ($componentName in $components.Keys)
+{
+ $cabUrl = GenerateFwlink($componentName)
+ $cabFile = GenerateCabPath($componentName)
+
+ # Download CAB file, if necessary
+ if (-not (IsCachedCabValid $cabFile $cabUrl))
+ {
+ WriteMessage "Downloading $cabUrl to $cabFile"
+ Invoke-WebRequest -Uri $cabUrl -OutFile $cabFile
+ }
+}
+
+# Extract the contents of each CAB file
+foreach ($componentName in $components.Keys)
+{
+ $cabFile = GenerateCabPath($componentName)
+
+ # Extract all files using the built-in expand.exe tool
+ WriteMessage "Extracting $cabFile to $installFolder (this may take several minutes)"
+ &"$Env:WinDir\System32\expand.exe" $cabFile -F:* $installFolder > $null
+}
+
+# Create shortcut
+WriteMessage "Creating shortcut $jupyterShortcut"
+$shell = New-Object -comObject WScript.Shell
+$shortcut = $shell.CreateShortcut($jupyterShortcut)
+$shortcut.TargetPath = "$installFolder\Scripts\jupyter-notebook.exe"
+$shortcut.Arguments = "--notebook-dir `"$installFolder\samples`""
+$shortcut.Save()
+
+# Force shortcut to launch as admin
+$bytes = [System.IO.File]::ReadAllBytes($jupyterShortcut)
+$bytes[0x15] = $bytes[0x15] -bor 0x20
+[System.IO.File]::WriteAllBytes($JupyterShortcut, $bytes)
+
+WriteMessage "Installation complete"
diff --git a/samples/extensibility/Microsoft Python packages/9.3/Install-PyForMLS.ps1 b/samples/extensibility/Microsoft Python packages/9.3/Install-PyForMLS.ps1
new file mode 100644
index 0000000000..ed22aebdd5
--- /dev/null
+++ b/samples/extensibility/Microsoft Python packages/9.3/Install-PyForMLS.ps1
@@ -0,0 +1,323 @@
+#
+# Install-PyForMLS.ps1
+# Copyright (C) Microsoft. All rights reserved.
+#
+
+# See: https://learn.microsoft.com/sql/machine-learning/python/setup-python-client-tools-sql
+
+# Command-line parameters
+param
+(
+ [string]$InstallFolder = "$Env:ProgramFiles\Microsoft\PyForMLS",
+ [string]$CacheFolder = $Env:Temp,
+ [string]$JupyterShortcut = "$Env:ProgramData\Microsoft\Windows\Start Menu\Jupyter Notebook for Microsoft Machine Learning Server.lnk"
+)
+
+# Verify script is being run with elevated privileges
+if (!([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator))
+{
+ Write-Error "This script must be run as Administrator"
+ Exit 1
+}
+
+# Product version to install
+$productVersion = '9.3.0.0'
+
+# Default language to install (en-us)
+$productLcid = '1033'
+
+# Component names and their corresponding fwlink IDs
+$components = @{ 'SPO' = '859054'; 'SPS' = '859056'; }
+
+# Helper function which outputs the specified message to the console and prepends the current date and time
+function WriteMessage($message)
+{
+ Write-Host ("[{0}] {1}" -f (Get-Date -format u), $message)
+}
+
+# Helper function which generates a fwlink for the specified component
+function GenerateFwlink($componentName)
+{
+ return 'https://go.microsoft.com/fwlink/?LinkId={0}&clcid={1}' -f $components[$componentName], $productLcid
+}
+
+# Helper function which generates the absolute CAB path of the specified component
+function GenerateCabPath($componentName)
+{
+ return "{0}\{1}_{2}_1033.cab" -f $cacheFolder, $componentName, $productVersion
+}
+
+# Helper function which returns true if the specified CAB file is found in the cache
+function IsCachedCabValid($cabFile, $cabUrl)
+{
+ $isValid = $false
+
+ # Does CAB file exist?
+ if (Test-Path $cabFile)
+ {
+ # Retrieve headers from CAB URL
+ $response = Invoke-WebRequest -Method Head -Uri $cabUrl
+
+ # Compare last modified date of cached file against the remote file
+ if ([datetime](Get-ItemProperty -Path $cabFile).LastWriteTime -ge $response.Headers['Last-Modified'])
+ {
+ WriteMessage "Cached $cabFile is up-to-date"
+ $isValid = $true
+ }
+ else
+ {
+ WriteMessage "Cached $cabFile is expired"
+ $isValid = $false
+ }
+ }
+ else
+ {
+ WriteMessage "$cabFile not found in cache"
+ $isValid = $false
+ }
+
+ return $isValid
+}
+
+WriteMessage "Starting installation"
+
+# Create install folder, if necessary
+if (-not (Test-Path $installFolder))
+{
+ WriteMessage "Creating install folder $installFolder"
+ New-Item -ItemType directory -Path $installFolder > $null
+}
+
+# Download the CAB file for each component
+foreach ($componentName in $components.Keys)
+{
+ $cabUrl = GenerateFwlink($componentName)
+ $cabFile = GenerateCabPath($componentName)
+
+ # Download CAB file, if necessary
+ if (-not (IsCachedCabValid $cabFile $cabUrl))
+ {
+ WriteMessage "Downloading $cabUrl to $cabFile"
+ Invoke-WebRequest -Uri $cabUrl -OutFile $cabFile
+ }
+}
+
+# Extract the contents of each CAB file
+foreach ($componentName in $components.Keys)
+{
+ $cabFile = GenerateCabPath($componentName)
+
+ # Extract all files using the built-in expand.exe tool
+ WriteMessage "Extracting $cabFile to $installFolder (this may take several minutes)"
+ &"$Env:WinDir\System32\expand.exe" $cabFile -F:* $installFolder > $null
+}
+
+# Create shortcut
+WriteMessage "Creating shortcut $jupyterShortcut"
+$shell = New-Object -comObject WScript.Shell
+$shortcut = $shell.CreateShortcut($jupyterShortcut)
+$shortcut.TargetPath = "$installFolder\Scripts\jupyter-notebook.exe"
+$shortcut.Arguments = "--notebook-dir `"$installFolder\samples`""
+$shortcut.Save()
+
+# Force shortcut to launch as admin
+$bytes = [System.IO.File]::ReadAllBytes($jupyterShortcut)
+$bytes[0x15] = $bytes[0x15] -bor 0x20
+[System.IO.File]::WriteAllBytes($JupyterShortcut, $bytes)
+
+WriteMessage "Installation complete"
+
+# SIG # Begin signature block
+# MIIj7gYJKoZIhvcNAQcCoIIj3zCCI9sCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCcQAtZ7Zu4h+GZ
+# d5+6oKbm+wLBylyrt7Epkx69JtzklaCCDYMwggYBMIID6aADAgECAhMzAAAAxOmJ
+# +HqBUOn/AAAAAADEMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
+# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
+# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
+# bmcgUENBIDIwMTEwHhcNMTcwODExMjAyMDI0WhcNMTgwODExMjAyMDI0WjB0MQsw
+# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
+# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
+# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+# AQCIirgkwwePmoB5FfwmYPxyiCz69KOXiJZGt6PLX4kvOjMuHpF4+nypH4IBtXrL
+# GrwDykbrxZn3+wQd8oUK/yJuofJnPcUnGOUoH/UElEFj7OO6FYztE5o13jhwVG87
+# 7K1FCTBJwb6PMJkMy3bJ93OVFnfRi7uUxwiFIO0eqDXxccLgdABLitLckevWeP6N
+# +q1giD29uR+uYpe/xYSxkK7WryvTVPs12s1xkuYe/+xxa8t/CHZ04BBRSNTxAMhI
+# TKMHNeVZDf18nMjmWuOF9daaDx+OpuSEF8HWyp8dAcf9SKcTkjOXIUgy+MIkogCy
+# vlPKg24pW4HvOG6A87vsEwvrAgMBAAGjggGAMIIBfDAfBgNVHSUEGDAWBgorBgEE
+# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUy9ZihM9gOer/Z8Jc0si7q7fDE5gw
+# UgYDVR0RBEswSaRHMEUxDTALBgNVBAsTBE1PUFIxNDAyBgNVBAUTKzIzMDAxMitj
+# ODA0YjVlYS00OWI0LTQyMzgtODM2Mi1kODUxZmEyMjU0ZmMwHwYDVR0jBBgwFoAU
+# SG5k5VAF04KqFzc3IrVtqMp1ApUwVAYDVR0fBE0wSzBJoEegRYZDaHR0cDovL3d3
+# dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljQ29kU2lnUENBMjAxMV8yMDEx
+# LTA3LTA4LmNybDBhBggrBgEFBQcBAQRVMFMwUQYIKwYBBQUHMAKGRWh0dHA6Ly93
+# d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljQ29kU2lnUENBMjAxMV8y
+# MDExLTA3LTA4LmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQAG
+# Fh/bV8JQyCNPolF41+34/c291cDx+RtW7VPIaUcF1cTL7OL8mVuVXxE4KMAFRRPg
+# mnmIvGar27vrAlUjtz0jeEFtrvjxAFqUmYoczAmV0JocRDCppRbHukdb9Ss0i5+P
+# WDfDThyvIsoQzdiCEKk18K4iyI8kpoGL3ycc5GYdiT4u/1cDTcFug6Ay67SzL1BW
+# XQaxFYzIHWO3cwzj1nomDyqWRacygz6WPldJdyOJ/rEQx4rlCBVRxStaMVs5apao
+# pIhrlihv8cSu6r1FF8xiToG1VBpHjpilbcBuJ8b4Jx/I7SCpC7HxzgualOJqnWmD
+# oTbXbSD+hdX/w7iXNgn+PRTBmBSpwIbM74LBq1UkQxi1SIV4htD50p0/GdkUieeN
+# n2gkiGg7qceATibnCCFMY/2ckxVNM7VWYE/XSrk4jv8u3bFfpENryXjPsbtrj4Ns
+# h3Kq6qX7n90a1jn8ZMltPgjlfIOxrbyjunvPllakeljLEkdi0iHv/DzEMQv3Lz5k
+# pTdvYFA/t0SQT6ALi75+WPbHZ4dh256YxMiMy29H4cAulO2x9rAwbexqSajplnbI
+# vQjE/jv1rnM3BrJWzxnUu/WUyocc8oBqAU+2G4Fzs9NbIj86WBjfiO5nxEmnL9wl
+# iz1e0Ow0RJEdvJEMdoI+78TYLaEEAo5I+e/dAs8DojCCB3owggVioAMCAQICCmEO
+# kNIAAAAAAAMwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
+# EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv
+# ZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmlj
+# YXRlIEF1dGhvcml0eSAyMDExMB4XDTExMDcwODIwNTkwOVoXDTI2MDcwODIxMDkw
+# OVowfjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT
+# B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UE
+# AxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMTCCAiIwDQYJKoZIhvcN
+# AQEBBQADggIPADCCAgoCggIBAKvw+nIQHC6t2G6qghBNNLrytlghn0IbKmvpWlCq
+# uAY4GgRJun/DDB7dN2vGEtgL8DjCmQawyDnVARQxQtOJDXlkh36UYCRsr55JnOlo
+# XtLfm1OyCizDr9mpK656Ca/XllnKYBoF6WZ26DJSJhIv56sIUM+zRLdd2MQuA3Wr
+# aPPLbfM6XKEW9Ea64DhkrG5kNXimoGMPLdNAk/jj3gcN1Vx5pUkp5w2+oBN3vpQ9
+# 7/vjK1oQH01WKKJ6cuASOrdJXtjt7UORg9l7snuGG9k+sYxd6IlPhBryoS9Z5JA7
+# La4zWMW3Pv4y07MDPbGyr5I4ftKdgCz1TlaRITUlwzluZH9TupwPrRkjhMv0ugOG
+# jfdf8NBSv4yUh7zAIXQlXxgotswnKDglmDlKNs98sZKuHCOnqWbsYR9q4ShJnV+I
+# 4iVd0yFLPlLEtVc/JAPw0XpbL9Uj43BdD1FGd7P4AOG8rAKCX9vAFbO9G9RVS+c5
+# oQ/pI0m8GLhEfEXkwcNyeuBy5yTfv0aZxe/CHFfbg43sTUkwp6uO3+xbn6/83bBm
+# 4sGXgXvt1u1L50kppxMopqd9Z4DmimJ4X7IvhNdXnFy/dygo8e1twyiPLI9AN0/B
+# 4YVEicQJTMXUpUMvdJX3bvh4IFgsE11glZo+TzOE2rCIF96eTvSWsLxGoGyY0uDW
+# iIwLAgMBAAGjggHtMIIB6TAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQUSG5k
+# 5VAF04KqFzc3IrVtqMp1ApUwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYD
+# VR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUci06AjGQQ7kU
+# BU7h6qfHMdEjiTQwWgYDVR0fBFMwUTBPoE2gS4ZJaHR0cDovL2NybC5taWNyb3Nv
+# ZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0MjAxMV8yMDExXzAz
+# XzIyLmNybDBeBggrBgEFBQcBAQRSMFAwTgYIKwYBBQUHMAKGQmh0dHA6Ly93d3cu
+# bWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0MjAxMV8yMDExXzAz
+# XzIyLmNydDCBnwYDVR0gBIGXMIGUMIGRBgkrBgEEAYI3LgMwgYMwPwYIKwYBBQUH
+# AgEWM2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvZG9jcy9wcmltYXJ5
+# Y3BzLmh0bTBABggrBgEFBQcCAjA0HjIgHQBMAGUAZwBhAGwAXwBwAG8AbABpAGMA
+# eQBfAHMAdABhAHQAZQBtAGUAbgB0AC4gHTANBgkqhkiG9w0BAQsFAAOCAgEAZ/KG
+# pZjgVHkaLtPYdGcimwuWEeFjkplCln3SeQyQwWVfLiw++MNy0W2D/r4/6ArKO79H
+# qaPzadtjvyI1pZddZYSQfYtGUFXYDJJ80hpLHPM8QotS0LD9a+M+By4pm+Y9G6XU
+# tR13lDni6WTJRD14eiPzE32mkHSDjfTLJgJGKsKKELukqQUMm+1o+mgulaAqPypr
+# WEljHwlpblqYluSD9MCP80Yr3vw70L01724lruWvJ+3Q3fMOr5kol5hNDj0L8giJ
+# 1h/DMhji8MUtzluetEk5CsYKwsatruWy2dsViFFFWDgycScaf7H0J/jeLDogaZiy
+# WYlobm+nt3TDQAUGpgEqKD6CPxNNZgvAs0314Y9/HG8VfUWnduVAKmWjw11SYobD
+# HWM2l4bf2vP48hahmifhzaWX0O5dY0HjWwechz4GdwbRBrF1HxS+YWG18NzGGwS+
+# 30HHDiju3mUv7Jf2oVyW2ADWoUa9WfOXpQlLSBCZgB/QACnFsZulP0V3HjXG0qKi
+# n3p6IvpIlR+r+0cjgPWe+L9rt0uX4ut1eBrs6jeZeRhL/9azI2h15q/6/IvrC4Dq
+# aTuv/DDtBEyO3991bWORPdGdVk5Pv4BXIqF4ETIheu9BCrE/+6jMpF3BoYibV3FW
+# TkhFwELJm3ZbCoBIa/15n8G9bW1qyVJzEw16UM0xghXBMIIVvQIBATCBlTB+MQsw
+# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
+# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNy
+# b3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExAhMzAAAAxOmJ+HqBUOn/AAAAAADE
+# MA0GCWCGSAFlAwQCAQUAoIGwMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwG
+# CisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCBFICAp
+# M2j0TOGgkpBNkVd+X/ZevrIdRG1Kc4YxAZ0uLjBEBgorBgEEAYI3AgEMMTYwNKAU
+# gBIATQBpAGMAcgBvAHMAbwBmAHShHIAaaHR0cHM6Ly93d3cubWljcm9zb2Z0LmNv
+# bSAwDQYJKoZIhvcNAQEBBQAEggEAX4lloFuwEEraoxJFk/wnE107hYIp7veGKPhB
+# Fd7aUOll/FY7emZeZ7Dbz2b1qbV6DmKboiSNRfqgfB0F3dMVUiBVtvtRdw5MAUqx
+# o/PKCD82KDZudLvcOkXDZLgfQSe2EKsrme96HFJ7D+s/vrGACewiixNXsZ7MMJ/K
+# pKwJF7kqD5/W7V+QzpgaYa9qYG4Iomrybua7VEeGhUg4islCHIwvX0naAAPY11gZ
+# ubZFGnC+w11dl1lQ1IzNga6Kxl857DygTiJIV7ODUP8eCELDCTgIUj3PXq04hTzn
+# mW1hzCBoqCTYinYSDZjVyg6mFxQUMIlp4w/210hSKivxCA8o0aGCE0kwghNFBgor
+# BgEEAYI3AwMBMYITNTCCEzEGCSqGSIb3DQEHAqCCEyIwghMeAgEDMQ8wDQYJYIZI
+# AWUDBAIBBQAwggE8BgsqhkiG9w0BCRABBKCCASsEggEnMIIBIwIBAQYKKwYBBAGE
+# WQoDATAxMA0GCWCGSAFlAwQCAQUABCBz8XcAnPXHLROng3eOI0UaWfW0nM5svgvd
+# BWnWX9tULAIGWwMpKo8OGBMyMDE4MDUyNDIwMDA1NS44NDRaMAcCAQGAAgPnoIG4
+# pIG1MIGyMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
+# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMQwwCgYD
+# VQQLEwNBT0MxJzAlBgNVBAsTHm5DaXBoZXIgRFNFIEVTTjo4NDNELTM3RjYtRjEw
+# NDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaCCDs0wggZx
+# MIIEWaADAgECAgphCYEqAAAAAAACMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQG
+# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
+# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQg
+# Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0xMDA3MDEyMTM2NTVa
+# Fw0yNTA3MDEyMTQ2NTVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n
+# dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y
+# YXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMIIB
+# IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqR0NvHcRijog7PwTl/X6f2mU
+# a3RUENWlCgCChfvtfGhLLF/Fw+Vhwna3PmYrW/AVUycEMR9BGxqVHc4JE458YTBZ
+# sTBED/FgiIRUQwzXTbg4CLNC3ZOs1nMwVyaCo0UN0Or1R4HNvyRgMlhgRvJYR4Yy
+# hB50YWeRX4FUsc+TTJLBxKZd0WETbijGGvmGgLvfYfxGwScdJGcSchohiq9LZIlQ
+# YrFd/XcfPfBXday9ikJNQFHRD5wGPmd/9WbAA5ZEfu/QS/1u5ZrKsajyeioKMfDa
+# TgaRtogINeh4HLDpmc085y9Euqf03GS9pAHBIAmTeM38vMDJRF1eFpwBBU8iTQID
+# AQABo4IB5jCCAeIwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFNVjOlyKMZDz
+# Q3t8RhvFM2hahW1VMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQE
+# AwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQ
+# W9fOmhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNv
+# bS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBa
+# BggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0
+# LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MIGgBgNV
+# HSABAf8EgZUwgZIwgY8GCSsGAQQBgjcuAzCBgTA9BggrBgEFBQcCARYxaHR0cDov
+# L3d3dy5taWNyb3NvZnQuY29tL1BLSS9kb2NzL0NQUy9kZWZhdWx0Lmh0bTBABggr
+# BgEFBQcCAjA0HjIgHQBMAGUAZwBhAGwAXwBQAG8AbABpAGMAeQBfAFMAdABhAHQA
+# ZQBtAGUAbgB0AC4gHTANBgkqhkiG9w0BAQsFAAOCAgEAB+aIUQ3ixuCYP4FxAz2d
+# o6Ehb7Prpsz1Mb7PBeKp/vpXbRkws8LFZslq3/Xn8Hi9x6ieJeP5vO1rVFcIK1GC
+# RBL7uVOMzPRgEop2zEBAQZvcXBf/XPleFzWYJFZLdO9CEMivv3/Gf/I3fVo/HPKZ
+# eUqRUgCvOA8X9S95gWXZqbVr5MfO9sp6AG9LMEQkIjzP7QOllo9ZKby2/QThcJ8y
+# Sif9Va8v/rbljjO7Yl+a21dA6fHOmWaQjP9qYn/dxUoLkSbiOewZSnFjnXshbcOc
+# o6I8+n99lmqQeKZt0uGc+R38ONiU9MalCpaGpL2eGq4EQoO4tYCbIjggtSXlZOz3
+# 9L9+Y1klD3ouOVd2onGqBooPiRa6YacRy5rYDkeagMXQzafQ732D8OE7cQnfXXSY
+# Ighh2rBQHm+98eEA3+cxB6STOvdlR3jo+KhIq/fecn5ha293qYHLpwmsObvsxsvY
+# grRyzR30uIUBHoD7G4kqVDmyW9rIDVWZeodzOwjmmC3qjeAzLhIp9cAvVCch98is
+# TtoouLGp25ayp0Kiyc8ZQU3ghvkqmqMRZjDTu3QyS99je/WZii8bxyGvWbWu3EQ8
+# l1Bx16HSxVXjad5XwdHeMMD9zOZN+w2/XU/pnR4ZOC+8z1gFLu8NoFA12u8JJxzV
+# s341Hgi62jbb01+P3nSISRIwggTZMIIDwaADAgECAhMzAAAAqVRw2XnAhGXiAAAA
+# AACpMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
+# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
+# cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw
+# MB4XDTE2MDkwNzE3NTY1M1oXDTE4MDkwNzE3NTY1M1owgbIxCzAJBgNVBAYTAlVT
+# MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK
+# ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xDDAKBgNVBAsTA0FPQzEnMCUGA1UECxMe
+# bkNpcGhlciBEU0UgRVNOOjg0M0QtMzdGNi1GMTA0MSUwIwYDVQQDExxNaWNyb3Nv
+# ZnQgVGltZS1TdGFtcCBTZXJ2aWNlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+# CgKCAQEArJSFGKblVtOd3wNnLtuYkUePlYlyTZp08zRA3msRp9THkn4O/581On+n
+# ZIxxm2HFGVk+lF2RL07A7cFAbicHkdTlrPYePM5QEVMnaITS0makH24deymLJuMJ
+# rnTnTPyfg7dGDdsVqQ37V/ezmxDeDBykTRrDliRGNimQXN4dR9aXP0KNB/+oLyeO
+# 6xIQsUdC9wS9OTbExbvA7La8joGcyd2yQDw9o+sbvTB1/lsFcx0UMRHU8Dq/7NET
+# 3kTJxP5I4VfELngIFX7zRQY2Sba1/VgdEd2IZANCEDnvrlMWRhFbXH0SWndIdnAp
+# YSEak1OcImlunLR5eo5MOIQVGWxfoQIDAQABo4IBGzCCARcwHQYDVR0OBBYEFA/V
+# Qfu78530vklS2ow3V85kD/N9MB8GA1UdIwQYMBaAFNVjOlyKMZDzQ3t8RhvFM2ha
+# hW1VMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9w
+# a2kvY3JsL3Byb2R1Y3RzL01pY1RpbVN0YVBDQV8yMDEwLTA3LTAxLmNybDBaBggr
+# BgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNv
+# bS9wa2kvY2VydHMvTWljVGltU3RhUENBXzIwMTAtMDctMDEuY3J0MAwGA1UdEwEB
+# /wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJKoZIhvcNAQELBQADggEBAAiZ
+# GKUHiy9yJHUsjiCEAv0Koa8O4bAyEYaqxYYgnbvoRDuVzLU654tGpRPTjCAbqDpX
+# HBX3c22NC7IHRW6GRXYkRrp0TPE2b1KdtuTklIzJKauJqr5ygtO6m1WroII54Bku
+# 2BGtRYkDS8Av4gCeuHuH28rXdbguBLSMkzeKHiZE5NlBZY7RQrleExC8GWd1u86E
+# qekfjnvPG5S4OV1tV1nsCn7G1pUNO+f6iC9WrFUEUHJnP7IAA8OOwvw+yJWr4NRn
+# tqY0bbRgCLJCid5/YNpYIbzTjDgyU/IKzNvfJLcA65NKPwl6NDtLwHNralKEU6Gb
+# BERZYUKtcvBAwG78mrKhggN3MIICXwIBATCB4qGBuKSBtTCBsjELMAkGA1UEBhMC
+# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
+# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEMMAoGA1UECxMDQU9DMScwJQYDVQQL
+# Ex5uQ2lwaGVyIERTRSBFU046ODQzRC0zN0Y2LUYxMDQxJTAjBgNVBAMTHE1pY3Jv
+# c29mdCBUaW1lLVN0YW1wIFNlcnZpY2WiJQoBATAJBgUrDgMCGgUAAxUAXTq/Vr3h
+# DynKcg5k93U7eBoi6/SggcEwgb6kgbswgbgxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
+# EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv
+# ZnQgQ29ycG9yYXRpb24xDDAKBgNVBAsTA0FPQzEnMCUGA1UECxMebkNpcGhlciBO
+# VFMgRVNOOjI2NjUtNEMzRi1DNURFMSswKQYDVQQDEyJNaWNyb3NvZnQgVGltZSBT
+# b3VyY2UgTWFzdGVyIENsb2NrMA0GCSqGSIb3DQEBBQUAAgUA3rEC7DAiGA8yMDE4
+# MDUyNDA5MjI1MloYDzIwMTgwNTI1MDkyMjUyWjB3MD0GCisGAQQBhFkKBAExLzAt
+# MAoCBQDesQLsAgEAMAoCAQACAhmJAgH/MAcCAQACAhmrMAoCBQDeslRsAgEAMDYG
+# CisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwGgCjAIAgEAAgMegJihCjAIAgEA
+# AgMehIAwDQYJKoZIhvcNAQEFBQADggEBACCC4U8Xw3EV8c3XIrkUGCA5fOqq33GG
+# gvCw8gXTlTglImU6Z5JbrziEP+02wW6RN/uvuWpfu+ErTvN5E7vxxj5Og/IFWmif
+# swF/N40pWeTJU1fyvo4BVYHygLyRpnGFCEeXT4sjXQSa1YLoaFcamTG/r7vt9W1s
+# nvJUfyW5KMX8xlnivFKM65RUYHZ1/zB/fA9K9MQtIFU0Jr3jYwCd0JVcnuysaN66
+# aU2EnpsoiTYmBL138s4hM7cK5vymzofp0eYVTd/83PP8H2ZTRGEm9sOglOewtNMs
+# fHwkpm7cZxPF5fsbxxYUGwM/mCl89dBGsdU+vjlIaiovX8yJKHUznnkxggL1MIIC
+# 8QIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G
+# A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYw
+# JAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAKlUcNl5
+# wIRl4gAAAAAAqTANBglghkgBZQMEAgEFAKCCATIwGgYJKoZIhvcNAQkDMQ0GCyqG
+# SIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCDEz3JAIekNn0Gtr62/eNmLmZoYbCcR
+# rmhTOaiTywyovDCB4gYLKoZIhvcNAQkQAgwxgdIwgc8wgcwwgbEEFF06v1a94Q8p
+# ynIOZPd1O3gaIuv0MIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh
+# c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD
+# b3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIw
+# MTACEzMAAACpVHDZecCEZeIAAAAAAKkwFgQUsmMPwmVuSiBbNg4ZDFfesXTSyM8w
+# DQYJKoZIhvcNAQELBQAEggEAgVub0c34ntVQ3x1wpzKcQgN+UswIw5fkKgkAjKNK
+# 4dRCJpcja9P15d3t72KA/dJOoGBNagloOyewROaGSaoKjBKzBsSEX9ADdAFicYgW
+# Oywr8cfChXdrwZNRHlOAcZV0G1TGWtaY5/Hb46KS2WZamBeraQ7+eqGsIoYrQyl5
+# quKcDvd+Msa8NQelc/ypudesYKUnE+96RIvJACR2CD99aYq22uWgXX75q9RgcUuE
+# PNCIfqdw36oeC6bz9/fHCORlfPMnzZYebJE7rw7utrg7m6SnL5dLQM2v3OnFwccq
+# jSE2VAQNJk/Z3FZnpXquy5pNbtuNcd3sYki5+d0OV4fcpQ==
+# SIG # End signature block
diff --git a/samples/extensibility/TutorialDB.bak b/samples/extensibility/TutorialDB.bak
new file mode 100644
index 0000000000..91da541693
Binary files /dev/null and b/samples/extensibility/TutorialDB.bak differ
diff --git a/samples/features/azure-arc/configuration/Patch-ComputeMachineDisableFeatures.ps1 b/samples/features/azure-arc/configuration/Patch-ComputeMachineDisableFeatures.ps1
new file mode 100644
index 0000000000..f86be2d4a7
--- /dev/null
+++ b/samples/features/azure-arc/configuration/Patch-ComputeMachineDisableFeatures.ps1
@@ -0,0 +1,144 @@
+param (
+[string]$resourceGroupName,
+[Parameter(Mandatory=$true)]
+[string]$subscriptionId,
+[bool]$whatIf = $false
+)
+
+if ([string]::IsNullOrEmpty($subscriptionId)) {
+ $subscriptionId = Read-Host -Prompt "Please enter the subscription ID"
+}
+
+# if ([string]::IsNullOrEmpty($resourceGroupName)) {
+# $resourceGroupName = Read-Host -Prompt "Please enter the resource group name"
+# }
+
+if ([string]::IsNullOrEmpty($whatIf)) {
+ $dryRun = Read-Host -Prompt "Would you like to run this as a dry run? (yes/no)"
+ if ($dryRun -eq "yes") {
+ $whatIf = $true
+ } else {
+ $whatIf = $false
+ }
+}
+
+Write-Host "Resource Group Name: $resourceGroupName"
+Write-Host "Subscription ID: $subscriptionId"
+Write-Host "WhatIf: $WhatIf"
+
+if ([string]::IsNullOrEmpty($subscriptionId)) {
+ Write-Host "The subscription ID is required."
+ exit
+}
+
+$minSupportedApiVersion = '2024-07-10'
+$query = $null
+$resources = $null
+
+if ([string]::IsNullOrEmpty($resourceGroupName)){
+ $query ="
+ Resources
+ | where type =~ 'microsoft.azurearcdata/sqlserverinstances'
+ | where subscriptionId =~ '$subscriptionId'
+ | project name, resourceGroup
+ "
+} else {
+ $query ="
+ Resources
+ | where type =~ 'microsoft.azurearcdata/sqlserverinstances'
+ | where resourceGroup =~ '$resourceGroupName'
+ | where subscriptionId =~ '$subscriptionId'
+ | project name, resourceGroup
+ "
+}
+
+$resources = Search-AzGraph -Query $query
+
+if ([string]::IsNullOrEmpty($resources)) {
+ Write-Host "No SQL Server Instances were found in this scope."
+ exit
+}
+
+$resources = $resources | ForEach-Object {
+ [pscustomobject]@{
+ ResourceGroup = $_.resourceGroup
+ SqlArcResource = $_.name
+ }
+}
+
+$arcMachineResourceIds = @()
+foreach ($resource in $resources) {
+ Write-Host "ResourceGroup: $($resource.ResourceGroup), Sql Arc resource: $($resource.SqlArcResource)"
+
+ $hybridComputeResourceId = Get-AzResource -ResourceName $resource.SqlArcResource -ResourceGroupName $resource.ResourceGroup -ResourceType "Microsoft.AzureArcData/sqlServerInstances" | Select-Object -ExpandProperty Properties | Select-Object -ExpandProperty containerResourceId
+ $arcMachineResourceIds += $hybridComputeResourceId
+}
+
+$arcMachineUniqueResourceIds = $arcMachineResourceIds | Get-Unique
+Write-Output "Arc Machine Resource Ids:"
+Write-Output $arcMachineUniqueResourceIds
+
+foreach ($arcMachineUniqueResourceId in $arcMachineUniqueResourceIds) {
+ Write-Host "----- Attempting to remove settings from machine: $($arcMachineUniqueResourceId) -----"
+ $computeMachineResource = Get-AzResource -ResourceId "$arcMachineUniqueResourceId"
+ $extensionResource = Get-AzResource -ResourceId "$arcMachineUniqueResourceId/extensions/WindowsAgent.SqlServer" -ApiVersion $minSupportedApiVersion
+ $currentSettings = $extensionResource.properties.settings
+
+ $parsedData = @{
+ "ExtensionAgentStatus" = $null
+ "TimestampUTC" = $null
+ }
+
+ if ($extensionResource.properties.instanceView.status -match "SQL Server Extension Agent: (\w+);") {
+ $parsedData["ExtensionAgentStatus"] = $matches[1]
+ }
+
+ if ($extensionResource.properties.instanceView.status -match "timestampUTC : ([\d\/:., ]+);") {
+ $parsedData["TimestampUTC"] = [datetime]::ParseExact($matches[1], "yyyy/MM/dd, HH:mm:ss.fff", $null)
+ }
+
+ # Check if the Extension Agent is healthy and the timestamp is within the last 24 hours
+ $extensionAgentHealthy = $parsedData["ExtensionAgentStatus"] -eq "Healthy"
+ $timestampWithin24Hours = ($parsedData["TimestampUTC"] -gt (Get-Date).AddHours(-24))
+
+ if ($computeMachineResource.properties.status -ne "Connected") {
+ Write-Host "This machine has status: $($computeMachineResource.properties.status). We will skip removing the configurations on this machine."
+ continue
+ } elseif (-not ($extensionAgentHealthy -and $timestampWithin24Hours)) {
+ Write-Host "The extension agent status is: $($parsedData["ExtensionAgentStatus"]) and was last updated: $($($parsedData["TimestampUTC"]))."
+ Write-Host "The extension status must be healthy and updated within 24hrs for us to proceed. We will skip removing the configurations on this machine."
+ continue
+ } else {
+ Write-Host "This machine has status: $($computeMachineResource.properties.status). We will proceed to remove the configurations."
+ }
+
+ # Disable ESU
+ if ($currentSettings.PSobject.Properties.Name -contains "EnableExtendedSecurityUpdates") {
+ $currentSettings.EnableExtendedSecurityUpdates = $false
+ }
+ # Disable Microsoft Updates
+ if ($currentSettings.PSobject.Properties.Name -contains "MicrosoftUpdateConfiguration") {
+ $currentSettings.MicrosoftUpdateConfiguration.EnableMicrosoftUpdate = $false
+ }
+ # Disable BPA
+ if ($currentSettings.PSobject.Properties.Name -contains "AssessmentSettings") {
+ $currentSettings.AssessmentSettings.Enable = $false
+ }
+
+ $newProperties = $extensionResource.properties
+ $newProperties.settings = $currentSettings
+
+ $newProperties | ConvertTo-Json | Out-File "settingsInFile.json"
+
+ try {
+ if ($whatIf) {
+ $extensionResource | Set-AzResource -Properties $newProperties -UsePatchSemantics -Pre -ErrorAction Stop -WhatIf -AsJob
+ } else {
+ $extensionResource | Set-AzResource -Properties $newProperties -UsePatchSemantics -Pre -ErrorAction Stop -Force -AsJob
+ }
+ Write-Host "Command executed."
+ } catch {
+ Write-Host "Command failed with the following error:"
+ Write-Host $_.Exception.Message
+ }
+}
\ No newline at end of file
diff --git a/samples/features/azure-arc/configuration/Patch-SQLServerInstance.ps1 b/samples/features/azure-arc/configuration/Patch-SQLServerInstance.ps1
new file mode 100644
index 0000000000..f7c3e7749b
--- /dev/null
+++ b/samples/features/azure-arc/configuration/Patch-SQLServerInstance.ps1
@@ -0,0 +1,57 @@
+param (
+ [string]$resourceGroupName = '',
+ [Parameter(Mandatory=$true)]
+ [string]$subscriptionId = '00000000-0000-0000-0000-000000000000',
+ [switch]$WhatIf = $false,
+ [string]$propertiesJSON = @"
+{
+ "backupPolicy": null,
+ "monitoring": {
+ "enabled": false
+ },
+ "migration": {
+ "assessment": {
+ "enabled": false
+ }
+ }
+}
+"@
+)
+
+Write-Verbose "Resource Group Name: $resourceGroupName"
+Write-Verbose "Subscription ID: $subscriptionId"
+Write-Verbose "WhatIf: $WhatIf"
+Write-Verbose "Properties JSON: $propertiesJSON"
+
+$properties = $propertiesJSON | ConvertFrom-Json
+
+try {
+ Write-Verbose "Connecting to Azure with subscription ID: $subscriptionId"
+ $defaultProfile = Connect-AzAccount -SubscriptionId $subscriptionId -ErrorAction Stop
+
+ if ([string]::IsNullOrEmpty($resourceGroupName)) {
+ Write-Verbose "Fetching resources for subscription ID: $subscriptionId"
+ $resources = Get-AzResource -ResourceType "Microsoft.AzureArcData/SqlServerInstances" -ErrorAction Stop -Pre -ExpandProperties
+ } else {
+ Write-Verbose "Fetching resources for subscription ID: $subscriptionId and resource group: $resourceGroupName"
+ $resources = Get-AzResource -ResourceType "Microsoft.AzureArcData/SqlServerInstances" -ErrorAction Stop -Pre -ExpandProperties -ResourceGroupName $resourceGroupName
+ }
+ $resources = $resources | Where-Object { 'SSIS','SSAS','SSRS' -notcontains $_.Properties.serviceType }
+
+ foreach ($resource in $resources) {
+ try {
+ if ($WhatIf) {
+ Write-Verbose "Performing dry-run patch for resource: $($resource.Id)"
+ $resource | Set-AzResource -Properties $properties -UsePatchSemantics -Pre -Force -DefaultProfile $defaultProfile -ErrorAction Stop -WhatIf
+ } else {
+ Write-Verbose "Patching resource: $($resource.Id)"
+ $resource | Set-AzResource -Properties $properties -UsePatchSemantics -Pre -Force -DefaultProfile $defaultProfile -ErrorAction Stop
+ Write-Host("Resource patched: $($resource.Id)")
+ }
+ } catch {
+ Write-Error "Failed to patch resource: $($resource.Id). Error: $_"
+ }
+ }
+} catch {
+ Write-Error "An error occurred: $_"
+}
\ No newline at end of file
diff --git a/samples/features/azure-arc/dashboard/Arc - Deployment Progress.json b/samples/features/azure-arc/dashboard/Arc - Deployment Progress.json
index b3df74aa80..a037953023 100644
--- a/samples/features/azure-arc/dashboard/Arc - Deployment Progress.json
+++ b/samples/features/azure-arc/dashboard/Arc - Deployment Progress.json
@@ -1,10 +1,10 @@
{
"properties": {
- "lenses": {
- "0": {
+ "lenses": [
+ {
"order": 0,
- "parts": {
- "0": {
+ "parts": [
+ {
"position": {
"x": 0,
"y": 0,
@@ -25,7 +25,7 @@
}
}
},
- "1": {
+ {
"position": {
"x": 0,
"y": 2,
@@ -77,7 +77,7 @@
}
}
},
- "2": {
+ {
"position": {
"x": 3,
"y": 2,
@@ -129,7 +129,7 @@
}
}
},
- "3": {
+ {
"position": {
"x": 7,
"y": 2,
@@ -181,7 +181,7 @@
}
}
},
- "4": {
+ {
"position": {
"x": 11,
"y": 2,
@@ -233,7 +233,7 @@
}
}
},
- "5": {
+ {
"position": {
"x": 14,
"y": 2,
@@ -254,7 +254,7 @@
}
}
},
- "6": {
+ {
"position": {
"x": 0,
"y": 4,
@@ -302,11 +302,11 @@
"settings": {},
"partHeader": {
"title": "Windows Servers",
- "subtitle": "Count of Windws Servers registered to Arc"
+ "subtitle": "Count of Windows Servers registered to Arc"
}
}
},
- "7": {
+ {
"position": {
"x": 3,
"y": 4,
@@ -354,11 +354,11 @@
"settings": {},
"partHeader": {
"title": "Windows Servers Core Count",
- "subtitle": "Count of Windws Servers Cores registered to Arc"
+ "subtitle": "Count of Windows Servers Cores registered to Arc"
}
}
},
- "8": {
+ {
"position": {
"x": 7,
"y": 4,
@@ -410,7 +410,7 @@
}
}
},
- "9": {
+ {
"position": {
"x": 11,
"y": 4,
@@ -462,7 +462,7 @@
}
}
},
- "10": {
+ {
"position": {
"x": 0,
"y": 6,
@@ -514,7 +514,7 @@
}
}
},
- "11": {
+ {
"position": {
"x": 3,
"y": 6,
@@ -566,7 +566,7 @@
}
}
},
- "12": {
+ {
"position": {
"x": 7,
"y": 6,
@@ -618,7 +618,7 @@
}
}
},
- "13": {
+ {
"position": {
"x": 11,
"y": 6,
@@ -639,7 +639,7 @@
}
}
},
- "14": {
+ {
"position": {
"x": 0,
"y": 8,
@@ -691,7 +691,7 @@
}
}
},
- "15": {
+ {
"position": {
"x": 3,
"y": 8,
@@ -739,11 +739,11 @@
"settings": {},
"partHeader": {
"title": "Servers with SQL Server Ent/Std Edition",
- "subtitle": "Servers with at least one instance of Standard or Enterprisre Edition"
+ "subtitle": "Servers with at least one instance of Standard or Enterprise Edition"
}
}
},
- "16": {
+ {
"position": {
"x": 7,
"y": 8,
@@ -795,7 +795,7 @@
}
}
},
- "17": {
+ {
"position": {
"x": 11,
"y": 8,
@@ -816,7 +816,7 @@
}
}
},
- "18": {
+ {
"position": {
"x": 16,
"y": 8,
@@ -868,7 +868,7 @@
}
}
},
- "19": {
+ {
"position": {
"x": 0,
"y": 10,
@@ -915,12 +915,12 @@
"type": "Extension/HubsExtension/PartType/ArgQuerySingleValueTile",
"settings": {},
"partHeader": {
- "title": "Provisioned SQL Server Instasnces",
+ "title": "Provisioned SQL Server Instances",
"subtitle": "Count of SQL Server instances provisioned"
}
}
},
- "20": {
+ {
"position": {
"x": 3,
"y": 10,
@@ -972,7 +972,7 @@
}
}
},
- "21": {
+ {
"position": {
"x": 7,
"y": 10,
@@ -993,7 +993,7 @@
}
}
},
- "22": {
+ {
"position": {
"x": 0,
"y": 12,
@@ -1046,7 +1046,7 @@
}
}
},
- "23": {
+ {
"position": {
"x": 13,
"y": 12,
@@ -1099,7 +1099,7 @@
}
}
},
- "24": {
+ {
"position": {
"x": 0,
"y": 18,
@@ -1152,7 +1152,7 @@
}
}
},
- "25": {
+ {
"position": {
"x": 0,
"y": 24,
@@ -1205,7 +1205,7 @@
}
}
},
- "26": {
+ {
"position": {
"x": 0,
"y": 31,
@@ -1258,7 +1258,7 @@
}
}
},
- "27": {
+ {
"position": {
"x": 9,
"y": 31,
@@ -1310,7 +1310,7 @@
}
}
},
- "28": {
+ {
"position": {
"x": 0,
"y": 38,
@@ -1359,11 +1359,11 @@
"settings": {},
"partHeader": {
"title": "SQL Server Instances by Version",
- "subtitle": "SQL Server instasnces enabled by Azure Arc by SQL Server version"
+ "subtitle": "SQL Server instances enabled by Azure Arc by SQL Server version"
}
}
},
- "29": {
+ {
"position": {
"x": 9,
"y": 38,
@@ -1416,7 +1416,7 @@
}
}
},
- "30": {
+ {
"position": {
"x": 0,
"y": 45,
@@ -1468,7 +1468,7 @@
}
}
},
- "31": {
+ {
"position": {
"x": 0,
"y": 51,
@@ -1520,7 +1520,7 @@
}
}
},
- "32": {
+ {
"position": {
"x": 0,
"y": 56,
@@ -1572,7 +1572,7 @@
}
}
},
- "33": {
+ {
"position": {
"x": 0,
"y": 61,
@@ -1624,7 +1624,7 @@
}
}
},
- "34": {
+ {
"position": {
"x": 0,
"y": 67,
@@ -1678,9 +1678,9 @@
}
}
}
- }
+ ]
}
- },
+ ],
"metadata": {
"model": {
"timeRange": {
@@ -1701,5 +1701,5 @@
"tags": {
"hidden-title": "Arc - Deployment Progress"
},
- "apiVersion": "2015-08-01-preview"
+ "apiVersion": "2022-12-01-preview"
}
\ No newline at end of file
diff --git a/samples/features/azure-arc/dashboard/Arc - ESU.json b/samples/features/azure-arc/dashboard/Arc - ESU.json
index b42ac4d951..df1473054b 100644
--- a/samples/features/azure-arc/dashboard/Arc - ESU.json
+++ b/samples/features/azure-arc/dashboard/Arc - ESU.json
@@ -1,10 +1,10 @@
{
"properties": {
- "lenses": {
- "0": {
+ "lenses": [
+ {
"order": 0,
- "parts": {
- "0": {
+ "parts": [
+ {
"position": {
"x": 0,
"y": 0,
@@ -25,7 +25,7 @@
}
}
},
- "1": {
+ {
"position": {
"x": 0,
"y": 2,
@@ -46,7 +46,7 @@
}
}
},
- "2": {
+ {
"position": {
"x": 6,
"y": 2,
@@ -67,7 +67,7 @@
}
}
},
- "3": {
+ {
"position": {
"x": 12,
"y": 2,
@@ -88,7 +88,7 @@
}
}
},
- "4": {
+ {
"position": {
"x": 0,
"y": 3,
@@ -141,7 +141,7 @@
}
}
},
- "5": {
+ {
"position": {
"x": 6,
"y": 3,
@@ -194,7 +194,7 @@
}
}
},
- "6": {
+ {
"position": {
"x": 12,
"y": 3,
@@ -247,7 +247,7 @@
}
}
},
- "7": {
+ {
"position": {
"x": 0,
"y": 8,
@@ -300,7 +300,7 @@
}
}
},
- "8": {
+ {
"position": {
"x": 6,
"y": 8,
@@ -353,7 +353,7 @@
}
}
},
- "9": {
+ {
"position": {
"x": 12,
"y": 8,
@@ -406,7 +406,7 @@
}
}
},
- "10": {
+ {
"position": {
"x": 0,
"y": 13,
@@ -458,7 +458,7 @@
}
}
},
- "11": {
+ {
"position": {
"x": 12,
"y": 13,
@@ -511,7 +511,7 @@
}
}
},
- "12": {
+ {
"position": {
"x": 0,
"y": 18,
@@ -564,7 +564,7 @@
}
}
},
- "13": {
+ {
"position": {
"x": 0,
"y": 24,
@@ -616,7 +616,7 @@
}
}
},
- "14": {
+ {
"position": {
"x": 12,
"y": 24,
@@ -669,7 +669,7 @@
}
}
},
- "15": {
+ {
"position": {
"x": 0,
"y": 29,
@@ -721,7 +721,7 @@
}
}
},
- "16": {
+ {
"position": {
"x": 12,
"y": 29,
@@ -774,9 +774,9 @@
}
}
}
- }
+ ]
}
- },
+ ],
"metadata": {
"model": {
"timeRange": {
@@ -797,5 +797,5 @@
"tags": {
"hidden-title": "Arc - ESU"
},
- "apiVersion": "2015-08-01-preview"
+ "apiVersion": "2022-12-01-preview"
}
\ No newline at end of file
diff --git a/samples/features/azure-arc/dashboard/Arc - Estate Profile.json b/samples/features/azure-arc/dashboard/Arc - Estate Profile.json
index b6df9548e0..91d888f0d9 100644
--- a/samples/features/azure-arc/dashboard/Arc - Estate Profile.json
+++ b/samples/features/azure-arc/dashboard/Arc - Estate Profile.json
@@ -1,10 +1,10 @@
{
"properties": {
- "lenses": {
- "0": {
+ "lenses": [
+ {
"order": 0,
- "parts": {
- "0": {
+ "parts": [
+ {
"position": {
"x": 0,
"y": 0,
@@ -25,7 +25,7 @@
}
}
},
- "1": {
+ {
"position": {
"x": 0,
"y": 2,
@@ -77,7 +77,7 @@
}
}
},
- "2": {
+ {
"position": {
"x": 2,
"y": 2,
@@ -129,7 +129,7 @@
}
}
},
- "3": {
+ {
"position": {
"x": 4,
"y": 2,
@@ -181,7 +181,7 @@
}
}
},
- "4": {
+ {
"position": {
"x": 6,
"y": 2,
@@ -233,7 +233,7 @@
}
}
},
- "5": {
+ {
"position": {
"x": 8,
"y": 2,
@@ -285,7 +285,7 @@
}
}
},
- "6": {
+ {
"position": {
"x": 10,
"y": 2,
@@ -337,7 +337,7 @@
}
}
},
- "7": {
+ {
"position": {
"x": 12,
"y": 2,
@@ -389,7 +389,7 @@
}
}
},
- "8": {
+ {
"position": {
"x": 14,
"y": 2,
@@ -441,7 +441,7 @@
}
}
},
- "9": {
+ {
"position": {
"x": 16,
"y": 2,
@@ -493,7 +493,7 @@
}
}
},
- "10": {
+ {
"position": {
"x": 18,
"y": 2,
@@ -545,7 +545,7 @@
}
}
},
- "11": {
+ {
"position": {
"x": 21,
"y": 2,
@@ -597,7 +597,7 @@
}
}
},
- "12": {
+ {
"position": {
"x": 0,
"y": 5,
@@ -650,7 +650,7 @@
}
}
},
- "13": {
+ {
"position": {
"x": 0,
"y": 11,
@@ -703,7 +703,7 @@
}
}
},
- "14": {
+ {
"position": {
"x": 0,
"y": 17,
@@ -756,7 +756,7 @@
}
}
},
- "15": {
+ {
"position": {
"x": 9,
"y": 17,
@@ -811,7 +811,7 @@
}
}
},
- "16": {
+ {
"position": {
"x": 18,
"y": 17,
@@ -864,7 +864,7 @@
}
}
},
- "17": {
+ {
"position": {
"x": 0,
"y": 23,
@@ -916,7 +916,7 @@
}
}
},
- "18": {
+ {
"position": {
"x": 13,
"y": 23,
@@ -970,7 +970,7 @@
}
}
},
- "19": {
+ {
"position": {
"x": 16,
"y": 23,
@@ -1022,7 +1022,7 @@
}
}
},
- "20": {
+ {
"position": {
"x": 13,
"y": 26,
@@ -1074,7 +1074,7 @@
}
}
},
- "21": {
+ {
"position": {
"x": 0,
"y": 29,
@@ -1127,7 +1127,7 @@
}
}
},
- "22": {
+ {
"position": {
"x": 0,
"y": 34,
@@ -1180,7 +1180,7 @@
}
}
},
- "23": {
+ {
"position": {
"x": 0,
"y": 39,
@@ -1201,7 +1201,7 @@
}
}
},
- "24": {
+ {
"position": {
"x": 0,
"y": 40,
@@ -1254,7 +1254,7 @@
}
}
},
- "25": {
+ {
"position": {
"x": 0,
"y": 46,
@@ -1307,7 +1307,7 @@
}
}
},
- "26": {
+ {
"position": {
"x": 0,
"y": 52,
@@ -1360,7 +1360,7 @@
}
}
},
- "27": {
+ {
"position": {
"x": 0,
"y": 59,
@@ -1413,7 +1413,7 @@
}
}
},
- "28": {
+ {
"position": {
"x": 13,
"y": 59,
@@ -1466,7 +1466,7 @@
}
}
},
- "29": {
+ {
"position": {
"x": 0,
"y": 65,
@@ -1519,7 +1519,7 @@
}
}
},
- "30": {
+ {
"position": {
"x": 0,
"y": 71,
@@ -1572,7 +1572,7 @@
}
}
},
- "31": {
+ {
"position": {
"x": 0,
"y": 77,
@@ -1625,9 +1625,9 @@
}
}
}
- }
+ ]
}
- },
+ ],
"metadata": {
"model": {
"timeRange": {
@@ -1667,5 +1667,5 @@
"tags": {
"hidden-title": "Arc - Estate Profile"
},
- "apiVersion": "2015-08-01-preview"
+ "apiVersion": "2022-12-01-preview"
}
\ No newline at end of file
diff --git a/samples/features/azure-arc/dashboard/Arc - SQL Server Inventory.json b/samples/features/azure-arc/dashboard/Arc - SQL Server Inventory.json
index 03fb8669dc..7a10ebd428 100644
--- a/samples/features/azure-arc/dashboard/Arc - SQL Server Inventory.json
+++ b/samples/features/azure-arc/dashboard/Arc - SQL Server Inventory.json
@@ -1,10 +1,10 @@
{
"properties": {
- "lenses": {
- "0": {
+ "lenses": [
+ {
"order": 0,
- "parts": {
- "0": {
+ "parts": [
+ {
"position": {
"x": 0,
"y": 0,
@@ -25,7 +25,7 @@
}
}
},
- "1": {
+ {
"position": {
"x": 0,
"y": 2,
@@ -77,7 +77,7 @@
}
}
},
- "2": {
+ {
"position": {
"x": 3,
"y": 2,
@@ -129,7 +129,7 @@
}
}
},
- "3": {
+ {
"position": {
"x": 6,
"y": 2,
@@ -181,7 +181,7 @@
}
}
},
- "4": {
+ {
"position": {
"x": 0,
"y": 6,
@@ -234,7 +234,7 @@
}
}
},
- "5": {
+ {
"position": {
"x": 0,
"y": 11,
@@ -287,7 +287,7 @@
}
}
},
- "6": {
+ {
"position": {
"x": 0,
"y": 16,
@@ -336,11 +336,11 @@
"settings": {},
"partHeader": {
"title": "Compatibility Level",
- "subtitle": "Count of databases by compatibilty level"
+ "subtitle": "Count of databases by compatabilty level"
}
}
},
- "7": {
+ {
"position": {
"x": 0,
"y": 21,
@@ -393,7 +393,7 @@
}
}
},
- "8": {
+ {
"position": {
"x": 6,
"y": 21,
@@ -446,7 +446,7 @@
}
}
},
- "9": {
+ {
"position": {
"x": 12,
"y": 21,
@@ -499,7 +499,7 @@
}
}
},
- "10": {
+ {
"position": {
"x": 0,
"y": 27,
@@ -548,11 +548,11 @@
"settings": {},
"partHeader": {
"title": "SQL Server Edition Summary",
- "subtitle": "Count of SQL Server instances by **instsance** license type and edition"
+ "subtitle": "Count of SQL Server instances by **instances** license type and edition"
}
}
},
- "11": {
+ {
"position": {
"x": 12,
"y": 27,
@@ -606,7 +606,7 @@
}
}
},
- "12": {
+ {
"position": {
"x": 0,
"y": 34,
@@ -627,7 +627,7 @@
}
}
},
- "13": {
+ {
"position": {
"x": 0,
"y": 35,
@@ -680,7 +680,7 @@
}
}
},
- "14": {
+ {
"position": {
"x": 0,
"y": 42,
@@ -735,7 +735,7 @@
}
}
},
- "15": {
+ {
"position": {
"x": 0,
"y": 49,
@@ -790,7 +790,7 @@
}
}
},
- "16": {
+ {
"position": {
"x": 0,
"y": 56,
@@ -845,9 +845,9 @@
}
}
}
- }
+ ]
}
- },
+ ],
"metadata": {
"model": {
"timeRange": {
@@ -868,5 +868,5 @@
"tags": {
"hidden-title": "Arc - SQL Server Inventory"
},
- "apiVersion": "2015-08-01-preview"
+ "apiVersion": "2022-12-01-preview"
}
diff --git a/samples/features/azure-arc/dashboard/Arc - Server Deployment.json b/samples/features/azure-arc/dashboard/Arc - Server Deployment.json
index a6033ae83d..b2067900f3 100644
--- a/samples/features/azure-arc/dashboard/Arc - Server Deployment.json
+++ b/samples/features/azure-arc/dashboard/Arc - Server Deployment.json
@@ -1,10 +1,10 @@
{
"properties": {
- "lenses": {
- "0": {
+ "lenses": [
+ {
"order": 0,
- "parts": {
- "0": {
+ "parts": [
+ {
"position": {
"x": 0,
"y": 0,
@@ -56,7 +56,7 @@
}
}
},
- "1": {
+ {
"position": {
"x": 3,
"y": 0,
@@ -108,7 +108,7 @@
}
}
},
- "2": {
+ {
"position": {
"x": 0,
"y": 4,
@@ -161,7 +161,7 @@
}
}
},
- "3": {
+ {
"position": {
"x": 3,
"y": 4,
@@ -213,7 +213,7 @@
}
}
},
- "4": {
+ {
"position": {
"x": 0,
"y": 8,
@@ -266,7 +266,7 @@
}
}
},
- "5": {
+ {
"position": {
"x": 3,
"y": 8,
@@ -318,7 +318,7 @@
}
}
},
- "6": {
+ {
"position": {
"x": 0,
"y": 12,
@@ -371,7 +371,7 @@
}
}
},
- "7": {
+ {
"position": {
"x": 3,
"y": 12,
@@ -424,7 +424,7 @@
}
}
},
- "8": {
+ {
"position": {
"x": 0,
"y": 16,
@@ -477,7 +477,7 @@
}
}
},
- "9": {
+ {
"position": {
"x": 10,
"y": 16,
@@ -531,7 +531,7 @@
}
}
},
- "10": {
+ {
"position": {
"x": 0,
"y": 20,
@@ -584,7 +584,7 @@
}
}
},
- "11": {
+ {
"position": {
"x": 10,
"y": 20,
@@ -638,7 +638,7 @@
}
}
},
- "12": {
+ {
"position": {
"x": 0,
"y": 24,
@@ -690,7 +690,7 @@
}
}
},
- "13": {
+ {
"position": {
"x": 0,
"y": 28,
@@ -744,7 +744,7 @@
}
}
},
- "14": {
+ {
"position": {
"x": 6,
"y": 28,
@@ -797,7 +797,7 @@
}
}
},
- "15": {
+ {
"position": {
"x": 12,
"y": 28,
@@ -851,7 +851,7 @@
}
}
},
- "16": {
+ {
"position": {
"x": 0,
"y": 33,
@@ -905,7 +905,7 @@
}
}
},
- "17": {
+ {
"position": {
"x": 6,
"y": 33,
@@ -959,7 +959,7 @@
}
}
},
- "18": {
+ {
"position": {
"x": 12,
"y": 33,
@@ -1012,7 +1012,7 @@
}
}
},
- "19": {
+ {
"position": {
"x": 0,
"y": 38,
@@ -1066,9 +1066,9 @@
}
}
}
- }
+ ]
}
- },
+ ],
"metadata": {
"model": {
"timeRange": {
@@ -1089,5 +1089,5 @@
"tags": {
"hidden-title": "Arc Server Deployment"
},
- "apiVersion": "2015-08-01-preview"
-}
+ "apiVersion": "2022-12-01-preview"
+}
\ No newline at end of file
diff --git a/samples/features/azure-arc/dashboard/Arc SQL Server Feature Status.json b/samples/features/azure-arc/dashboard/Arc SQL Server Feature Status.json
new file mode 100644
index 0000000000..d3183d58fa
--- /dev/null
+++ b/samples/features/azure-arc/dashboard/Arc SQL Server Feature Status.json
@@ -0,0 +1,559 @@
+{
+ "properties": {
+ "lenses": [
+ {
+ "order": 0,
+ "parts": [
+ {
+ "position": {
+ "x": 0,
+ "y": 0,
+ "colSpan": 11,
+ "rowSpan": 7
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "chartType",
+ "isOptional": true
+ },
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\r\n| where type == \"microsoft.azurearcdata/sqlserverinstances\"\r\n| project \r\nid, \r\nMigrationAssessment = iif(properties.migration.assessment.enabled == 'true', 'ENABLED', 'disabled'), \r\nMonitoring = iif(properties.monitoring.enabled == 'true', 'ENABLED', 'disabled'), \r\nBackupEnabled = iif(isempty(properties.backupPolicy.retentionPeriodDays),'disabled', iif(properties.backupPolicy.retentionPeriodDays == 0,'disabled','ENABLED'))\r\n| order by id asc",
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryGridTile",
+ "settings": {},
+ "partHeader": {
+ "title": "SQL Server instance features status",
+ "subtitle": "Shows enabled/disabled for each feature that is configured at the SQL Server instance level"
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 11,
+ "y": 0,
+ "colSpan": 6,
+ "rowSpan": 7
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\n| where type == \"microsoft.azurearcdata/sqlserverinstances\"\n| project \nid, \nMigrationAssessment = iif(properties.migration.assessment.enabled == 'true', 'Enabled', 'Disabled')\n| summarize count(id) by MigrationAssessment",
+ "isOptional": true
+ },
+ {
+ "name": "chartType",
+ "value": 2,
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryChartTile",
+ "settings": {},
+ "partHeader": {
+ "title": "Migration assessment",
+ "subtitle": ""
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 17,
+ "y": 0,
+ "colSpan": 6,
+ "rowSpan": 7
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\n| where type == \"microsoft.azurearcdata/sqlserverinstances\"\n| project \nid, \nMonitoring = iif(properties.monitoring.enabled == 'true', 'Enabled', 'Disabled')\n| summarize count(id) by Monitoring",
+ "isOptional": true
+ },
+ {
+ "name": "chartType",
+ "value": 2,
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryChartTile",
+ "settings": {},
+ "partHeader": {
+ "title": "Monitoring dashboards",
+ "subtitle": ""
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 23,
+ "y": 0,
+ "colSpan": 6,
+ "rowSpan": 7
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\n| where type == \"microsoft.azurearcdata/sqlserverinstances\"\n| project \nid, \nBackupEnabled = iif(isempty(properties.backupPolicy.retentionPeriodDays),'Disabled', iif(properties.backupPolicy.retentionPeriodDays == 0,'Disabled','Enabled'))\n| summarize count(id) by BackupEnabled",
+ "isOptional": true
+ },
+ {
+ "name": "chartType",
+ "value": 2,
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryChartTile",
+ "settings": {},
+ "partHeader": {
+ "title": "Backups",
+ "subtitle": ""
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 0,
+ "y": 7,
+ "colSpan": 11,
+ "rowSpan": 14
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "chartType",
+ "isOptional": true
+ },
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\r\n| where type == \"microsoft.hybridcompute/machines/extensions\"\r\n| where properties.type in (\"WindowsAgent.SqlServer\",\"LinuxAgent.SqlServer\")\r\n| project\r\nid,\r\nESU = iff(notnull(properties.settings.enableExtendedSecurityUpdates), iff(properties.settings.enableExtendedSecurityUpdates == true,\"ENABLED\",\"disabled\"), \"disabled\"),\r\nPurview = iff(notnull(properties.settings.ExternalPolicyBasedAuthorization),\"ENABLED\",\"disabled\"),\r\nEntra = iff(notnull(properties.settings.AzureAD),\"ENABLED\",\"disabled\"),\r\nBPA = iff(notnull(properties.settings.AssessmentSettings),\"ENABLED\",\"disabled\"),\r\nAutomaticUpdate = iff(properties.settings.MicrosoftUpdateConfiguration.EnableMicrosoftUpdate == true, \"ENABLED\", \"disabled\")\r\n| order by id asc",
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryGridTile",
+ "settings": {},
+ "partHeader": {
+ "title": "SQL Server extension feature status",
+ "subtitle": "Shows the enabled and disabled status of each feature at the SQL Server extension level"
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 11,
+ "y": 7,
+ "colSpan": 6,
+ "rowSpan": 7
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\n| where type == \"microsoft.hybridcompute/machines/extensions\"\n| where properties.type in (\"WindowsAgent.SqlServer\",\"LinuxAgent.SqlServer\")\n| project id, ESU = iff(notnull(properties.settings.enableExtendedSecurityUpdates), iff(properties.settings.enableExtendedSecurityUpdates == true,\"Enabled\",\"Disabled\"), \"Disabled\")\n| summarize count(id) by ESU\n",
+ "isOptional": true
+ },
+ {
+ "name": "chartType",
+ "value": 2,
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryChartTile",
+ "settings": {},
+ "partHeader": {
+ "title": "Extended Security Updates",
+ "subtitle": ""
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 17,
+ "y": 7,
+ "colSpan": 6,
+ "rowSpan": 7
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\n| where type == \"microsoft.hybridcompute/machines/extensions\"\n| where properties.type in (\"WindowsAgent.SqlServer\",\"LinuxAgent.SqlServer\")\n| project id, AutomaticUpdates = iff(properties.settings.MicrosoftUpdateConfiguration.EnableMicrosoftUpdate == true, \"Enabled\", \"Disabled\")\n| summarize count(id) by AutomaticUpdates",
+ "isOptional": true
+ },
+ {
+ "name": "chartType",
+ "value": 2,
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryChartTile",
+ "settings": {},
+ "partHeader": {
+ "title": "Automatic Updates",
+ "subtitle": ""
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 23,
+ "y": 7,
+ "colSpan": 6,
+ "rowSpan": 7
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\n| where type == \"microsoft.hybridcompute/machines/extensions\"\n| where properties.type in (\"WindowsAgent.SqlServer\",\"LinuxAgent.SqlServer\")\n| project id, Purview = iff(notnull(properties.settings.ExternalPolicyBasedAuthorization), iff(properties.settings.enableExtendedSecurityUpdates == true,\"Enabled\",\"Disabled\"), \"Disabled\")\n| summarize count(id) by Purview",
+ "isOptional": true
+ },
+ {
+ "name": "chartType",
+ "value": 2,
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryChartTile",
+ "settings": {},
+ "partHeader": {
+ "title": "Purview",
+ "subtitle": ""
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 11,
+ "y": 14,
+ "colSpan": 6,
+ "rowSpan": 7
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\n| where type == \"microsoft.hybridcompute/machines/extensions\"\n| where properties.type in (\"WindowsAgent.SqlServer\",\"LinuxAgent.SqlServer\")\n| project id, Entra = iff(notnull(properties.settings.AzureAD), iff(properties.settings.enableExtendedSecurityUpdates == true,\"Enabled\",\"Disabled\"), \"Disabled\")\n| summarize count(id) by Entra",
+ "isOptional": true
+ },
+ {
+ "name": "chartType",
+ "value": 2,
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryChartTile",
+ "settings": {},
+ "partHeader": {
+ "title": "Entra ID authentication",
+ "subtitle": ""
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 17,
+ "y": 14,
+ "colSpan": 6,
+ "rowSpan": 7
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\n| where type == \"microsoft.hybridcompute/machines/extensions\"\n| where properties.type in (\"WindowsAgent.SqlServer\",\"LinuxAgent.SqlServer\")\n| project id, BPA = iff(notnull(properties.settings.AssessmentSettings), iff(properties.settings.enableExtendedSecurityUpdates == true,\"Enabled\",\"Disabled\"), \"Disabled\")\n| summarize count(id) by BPA",
+ "isOptional": true
+ },
+ {
+ "name": "chartType",
+ "value": 2,
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryChartTile",
+ "settings": {},
+ "partHeader": {
+ "title": "Best practices assessment",
+ "subtitle": ""
+ }
+ }
+ }
+ ]
+ }
+ ],
+ "metadata": {
+ "model": {
+ "timeRange": {
+ "value": {
+ "relative": {
+ "duration": 24,
+ "timeUnit": 1
+ }
+ },
+ "type": "MsPortalFx.Composition.Configuration.ValueTypes.TimeRange"
+ }
+ }
+ }
+ },
+ "name": "Arc-enabled SQL Server Features",
+ "type": "Microsoft.Portal/dashboards",
+ "location": "INSERT LOCATION",
+ "tags": {
+ "hidden-title": "Arc-enabled SQL Server Features"
+ },
+ "apiVersion": "2022-12-01-preview"
+}
\ No newline at end of file
diff --git a/samples/features/azure-arc/dashboard/Arc-enabled SQL Server Health.json b/samples/features/azure-arc/dashboard/Arc-enabled SQL Server Health.json
new file mode 100644
index 0000000000..518ea88ae6
--- /dev/null
+++ b/samples/features/azure-arc/dashboard/Arc-enabled SQL Server Health.json
@@ -0,0 +1,716 @@
+{
+ "properties": {
+ "lenses": [
+ {
+ "order": 0,
+ "parts": [
+ {
+ "position": {
+ "x": 0,
+ "y": 0,
+ "colSpan": 6,
+ "rowSpan": 2
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "chartType",
+ "isOptional": true
+ },
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\n| where type == \"microsoft.hybridcompute/machines\"\n| where properties.detectedProperties.mssqldiscovered =~ 'true' or properties.mssqldiscovered =~ 'true'\n| summarize SqlServerMachines = count()",
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQuerySingleValueTile",
+ "settings": {},
+ "partHeader": {
+ "title": "Machines Hosting SQL Server",
+ "subtitle": "The count of machines that have SQL Server installed"
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 6,
+ "y": 0,
+ "colSpan": 6,
+ "rowSpan": 2
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "chartType",
+ "isOptional": true
+ },
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\n| where type == \"microsoft.hybridcompute/machines/extensions\" \n| where properties.type in (\"WindowsAgent.SqlServer\", \"LinuxAgent.SqlServer\")\n| summarize SQLServerExtensions = count()",
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQuerySingleValueTile",
+ "settings": {},
+ "partHeader": {
+ "title": "SQL Server Extensions",
+ "subtitle": "The count of SQL Server extensions. Should be the same as the count of Machines Hosting SQL Server."
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 12,
+ "y": 0,
+ "colSpan": 6,
+ "rowSpan": 2
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "chartType",
+ "isOptional": true
+ },
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\n| where type == \"microsoft.hybridcompute/machines/extensions\"\n| where properties.type in (\"WindowsAgent.SqlServer\", \"LinuxAgent.SqlServer\")\n| extend targetMachineName = tolower(tostring(split(id, '/')[8]))\n| join kind=leftouter (\n resources\n | where type == \"microsoft.hybridcompute/machines\"\n | project machineId = id, MachineName = name, subscriptionId, resourceGroup, MachineStatus = properties.status, MachineProvisioningStatus = properties.provisioningState, MachineErrors = properties.errorDetails\n) on $left.targetMachineName == $right.MachineName and $left.resourceGroup == $right.resourceGroup and $left.subscriptionId == $right.subscriptionId\n| extend extractedDate = todatetime(extract(\"timestampUTC : (\\\\d{4}-\\\\d{2}-\\\\d{2})\", 1, tostring(properties.instanceView.status.message)))\n| extend isNotInDateRange = extractedDate < ago(3d) or isnull(extractedDate)\n| where properties.instanceView.status.message !contains \"SQL Server Extension Agent: Healthy\"\n or isNotInDateRange\n or properties.instanceView.status.message !contains \"uploadStatus : OK\"\n or properties.provisioningState != \"Succeeded\"\n or MachineStatus != \"Connected\"\n| project ID = id, MachineName, ResourceGroup = resourceGroup, SubscriptionID = subscriptionId, Location = location, \n LastReportedExtensionHealth = iif(properties.instanceView.status.message contains \"SQL Server Extension Agent: Healthy\", \"Healthy\", \"Unhealthy\"),\n LastExtensionUploadTimestamp = tostring(extractedDate),\n LastExtensionUploadStatus = iif(properties.instanceView.status.message contains \"uploadStatus : OK\", \"OK\", \"Unhealthy\"),\n ExtensionProvisioningState = properties.provisioningState,\n MachineStatus, MachineErrors, MachineProvisioningStatus\n| project OverallHealth = iif(LastReportedExtensionHealth == \"Healthy\" and LastExtensionUploadStatus == \"OK\" and ExtensionProvisioningState == \"Succeeded\" and MachineStatus == \"Connected\" and MachineProvisioningStatus == \"Succeeded\", \"Healthy\", \"Unhealthy\")\n| where OverallHealth == \"Healthy\"\n| summarize HealthyExtensions = count()",
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQuerySingleValueTile",
+ "settings": {},
+ "partHeader": {
+ "title": "Overall Healthy SQL Server Extensions",
+ "subtitle": "The count of overall healthy SQL Server extensions should be the same as the count of SQL Server extensions."
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 0,
+ "y": 2,
+ "colSpan": 6,
+ "rowSpan": 4
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "chartType",
+ "value": 2,
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\n | where type =~ 'microsoft.hybridcompute/machines/extensions'\n | where properties.type in ('WindowsAgent.SqlServer','LinuxAgent.SqlServer')\n | parse id with * '/providers/Microsoft.HybridCompute/machines/' machineName '/extensions/' *\n | parse properties with * 'uploadStatus : ' uploadStatus ';' *\n | project uploadStatus, subscriptionId, resourceGroup, machineName\n | summarize count() by uploadStatus",
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryChartTile",
+ "settings": {},
+ "partHeader": {
+ "title": "SQL Extension DPS Upload Status",
+ "subtitle": "'OK' is good. Anaything else should be investigated."
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 6,
+ "y": 2,
+ "colSpan": 6,
+ "rowSpan": 4
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ },
+ {
+ "name": "chartType",
+ "value": 2,
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\n| where type =~ 'microsoft.hybridcompute/machines/extensions'\n| where properties.type in ('WindowsAgent.SqlServer', 'LinuxAgent.SqlServer')\n| parse id with * '/providers/Microsoft.HybridCompute/machines/' machineName '/extensions/' *\n| extend extractedDateString = extract(\"timestampUTC : (\\\\d{4}-\\\\d{2}-\\\\d{2})\", 1, tostring(properties.instanceView.status.message))\n| extend extractedDateString = case(\n isnull(extractedDateString) or extractedDateString == '', \n '1900-01-01T00:00:00Z', \n extractedDateString\n)\n| extend extractedDate = todatetime(extractedDateString)\n| extend isRecent = case(extractedDate > ago(1d), \"Less than 1 day ago\", extractedDate > ago(3d), \"1 to 3 days ago\", \"More than 3 days ago\")\n| summarize count() by isRecent",
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryChartTile",
+ "settings": {},
+ "partHeader": {
+ "title": "SQL Extension Status Update Time",
+ "subtitle": "Status should be updated every 5 minutes."
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 12,
+ "y": 2,
+ "colSpan": 6,
+ "rowSpan": 4
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "chartType",
+ "value": 2,
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\n| where type == \"microsoft.hybridcompute/machines/extensions\"\n| where properties.type in (\"WindowsAgent.SqlServer\", \"LinuxAgent.SqlServer\")\n| summarize count() by tostring(properties.provisioningState)",
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryChartTile",
+ "settings": {},
+ "partHeader": {
+ "title": "SQL Extension Provisioning State",
+ "subtitle": "Provisioning State should normally be 'Succeeded'"
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 0,
+ "y": 6,
+ "colSpan": 6,
+ "rowSpan": 4
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\n| where type == \"microsoft.hybridcompute/machines/extensions\" \n| where properties.type in (\"WindowsAgent.SqlServer\", \"LinuxAgent.SqlServer\")\n| summarize count() by tostring(properties.typeHandlerVersion)",
+ "isOptional": true
+ },
+ {
+ "name": "chartType",
+ "value": 2,
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryChartTile",
+ "settings": {},
+ "partHeader": {
+ "title": "SQL Extension Versions",
+ "subtitle": "Extension versions should be on a recent version, at least within one year. https://aka.ms/arcsqlreleasenotes"
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 6,
+ "y": 6,
+ "colSpan": 6,
+ "rowSpan": 4
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\n| where type == \"microsoft.hybridcompute/machines/extensions\"\n| where properties.type in (\"WindowsAgent.SqlServer\", \"LinuxAgent.SqlServer\")\n| project GeneralHealth = iif(properties.instanceView.status.message contains \"SQL Server Extension Agent: Healthy\", \"Healthy\", \"Unhealthy\")\n| summarize count() by GeneralHealth",
+ "isOptional": true
+ },
+ {
+ "name": "chartType",
+ "value": 2,
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryChartTile",
+ "settings": {},
+ "partHeader": {
+ "title": "SQL Extension General Health",
+ "subtitle": "The health as reported in the extension of the extension itself."
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 12,
+ "y": 6,
+ "colSpan": 6,
+ "rowSpan": 4
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\n| where type == \"microsoft.hybridcompute/machines/extensions\" \n| where properties.type in (\"WindowsAgent.SqlServer\", \"LinuxAgent.SqlServer\") \n| extend targetMachineName = tolower(tostring(split(id, '/')[8])) // Extract the machine name from the extension's id\n| join kind=leftouter (\n resources\n | where type == \"microsoft.hybridcompute/machines\"\n | project machineId = id, MachineName = name, subscriptionId, LowerMachineName = tolower(name), resourceGroup , MachineStatus= properties.status , MachineProvisioningStatus= properties.provisioningState\n) on $left.targetMachineName == $right.LowerMachineName and $left.resourceGroup == $right.resourceGroup and $left.subscriptionId == $right.subscriptionId // Join Based on MachineName in the id and the machine's name, the resource group, and the subscription. This join allows us to present the data of the machine as well as the extension in the final output.\n| summarize count() by tostring(MachineProvisioningStatus)",
+ "isOptional": true
+ },
+ {
+ "name": "chartType",
+ "value": 2,
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryChartTile",
+ "settings": {},
+ "partHeader": {
+ "title": "Machine Provisioning State",
+ "subtitle": "Machines should have a provisioning state of 'Succeeded'"
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 0,
+ "y": 10,
+ "colSpan": 6,
+ "rowSpan": 4
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\n| where type == \"microsoft.hybridcompute/machines/extensions\" \n| where properties.type in (\"WindowsAgent.SqlServer\", \"LinuxAgent.SqlServer\") \n| extend targetMachineName = tolower(tostring(split(id, '/')[8])) // Extract the machine name from the extension's id\n| join kind=leftouter (\n resources\n | where type == \"microsoft.hybridcompute/machines\"\n | project machineId = id, MachineName = name, subscriptionId, LowerMachineName = tolower(name), resourceGroup , MachineStatus= properties.status , MachineProvisioningStatus= properties.provisioningState\n) on $left.targetMachineName == $right.LowerMachineName and $left.resourceGroup == $right.resourceGroup and $left.subscriptionId == $right.subscriptionId // Join Based on MachineName in the id and the machine's name, the resource group, and the subscription. This join allows us to present the data of the machine as well as the extension in the final output.\n| summarize count() by tostring(MachineStatus)",
+ "isOptional": true
+ },
+ {
+ "name": "chartType",
+ "value": 2,
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryChartTile",
+ "settings": {},
+ "partHeader": {
+ "title": "Machine Connectivity Status",
+ "subtitle": "Machines should have a state of 'Connected'"
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 6,
+ "y": 10,
+ "colSpan": 6,
+ "rowSpan": 4
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "chartType",
+ "value": 2,
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\n| where type == \"microsoft.hybridcompute/machines/extensions\" \n| where properties.type in (\"WindowsAgent.SqlServer\", \"LinuxAgent.SqlServer\") \n| extend targetMachineName = tolower(tostring(split(id, '/')[8])) // Extract the machine name from the extension's id\n| join kind=leftouter (\n resources\n | where type == \"microsoft.hybridcompute/machines\"\n | project machineId = id, MachineName = name, subscriptionId, LowerMachineName = tolower(name), resourceGroup , MachineExtensionServiceStatus = properties.serviceStatuses.extensionService.status\n) on $left.targetMachineName == $right.LowerMachineName and $left.resourceGroup == $right.resourceGroup and $left.subscriptionId == $right.subscriptionId // Join Based on MachineName in the id and the machine's name, the resource group, and the subscription. This join allows us to present the data of the machine as well as the extension in the final output.\n| summarize count() by tostring(MachineExtensionServiceStatus)",
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryChartTile",
+ "settings": {},
+ "partHeader": {
+ "title": "Machine Extension Service Status",
+ "subtitle": "The Machine Extension Service status should be 'Running'"
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 12,
+ "y": 10,
+ "colSpan": 6,
+ "rowSpan": 4
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "//Machine Guest Configuration Service Status\nresources\n| where type == \"microsoft.hybridcompute/machines/extensions\" \n| where properties.type in (\"WindowsAgent.SqlServer\", \"LinuxAgent.SqlServer\") \n| extend targetMachineName = tolower(tostring(split(id, '/')[8])) // Extract the machine name from the extension's id\n| join kind=leftouter (\n resources\n | where type == \"microsoft.hybridcompute/machines\"\n | project machineId = id, MachineName = name, subscriptionId, LowerMachineName = tolower(name), resourceGroup , GuestConfigurationService = properties.serviceStatuses.guestConfigurationService.status\n) on $left.targetMachineName == $right.LowerMachineName and $left.resourceGroup == $right.resourceGroup and $left.subscriptionId == $right.subscriptionId // Join Based on MachineName in the id and the machine's name, the resource group, and the subscription. This join allows us to present the data of the machine as well as the extension in the final output.\n| summarize count() by tostring(GuestConfigurationService)",
+ "isOptional": true
+ },
+ {
+ "name": "chartType",
+ "value": 2,
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryChartTile",
+ "settings": {},
+ "partHeader": {
+ "title": "Machine Guest Configuration Service Status",
+ "subtitle": "The Machine Guest Configuration Status should be 'Running'"
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 0,
+ "y": 14,
+ "colSpan": 18,
+ "rowSpan": 12
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "chartType",
+ "isOptional": true
+ },
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\r\n| where type == \"microsoft.hybridcompute/machines/extensions\" \r\n| where properties.type in (\"WindowsAgent.SqlServer\", \"LinuxAgent.SqlServer\") \r\n| extend targetMachineName = tolower(tostring(split(id, '/')[8])) // Extract the machine name from the extension's id\r\n| join kind=leftouter (\r\n resources\r\n | where type == \"microsoft.hybridcompute/machines\"\r\n | project machineId = id, MachineName = name, subscriptionId, LowerMachineName = tolower(name), resourceGroup , MachineStatus= properties.status , MachineProvisioningStatus= properties.provisioningState, MachineErrors = properties.errorDetails //Project relvant machine health information.\r\n) on $left.targetMachineName == $right.LowerMachineName and $left.resourceGroup == $right.resourceGroup and $left.subscriptionId == $right.subscriptionId // Join Based on MachineName in the id and the machine's name, the resource group, and the subscription. This join allows us to present the data of the machine as well as the extension in the final output.\r\n| extend statusExpirationLengthRange = 3d // Change this value to change the acceptable range for the last time an extension should have reported its status.\r\n| extend startDate = startofday(now() - statusExpirationLengthRange), endDate = startofday(now()) // Get the start and end positon for the given range.\r\n| extend extractedDateString = extract(\"timestampUTC : (\\\\d{4}\\\\W\\\\d{2}\\\\W\\\\d{2})\", 1, tostring(properties.instanceView.status.message)) // Extracting the date string for the LastUploadTimestamp. Is empty if none is found.\r\n| extend extractedDateStringYear = split(extractedDateString, '/')[0], extractedDateStringMonth = split(extractedDateString, '/')[1], extractedDateStringDay = split(extractedDateString, '/')[2] // Identifying each of the parts of the date that was extracted from the message.\r\n| extend extractedDate = todatetime(strcat(extractedDateStringYear,\"-\",extractedDateStringMonth,\"-\",extractedDateStringDay,\"T00:00:00Z\")) // Converting to a datetime object and rewriting string into ISO format because todatetime() does not work using the previous format.\r\n| extend isNotInDateRange = not(extractedDate >= startDate and extractedDate <= endDate) // Created bool which is true if the date we extracted from the message is not within the specified range. This bool will also be true if the date was not found in the message.\r\n| where properties.instanceView.status.message !contains \"SQL Server Extension Agent: Healthy\" // Begin searching for unhealthy extensions using the following 1. Does extension report being healthy. 2. Is last upload within the given range. 3. Is the upload status in an OK state. 4. Is provisioning state not in a succeeded state.\r\n or isNotInDateRange\r\n or properties.instanceView.status.message !contains \"uploadStatus : OK\"\r\n or properties.provisioningState != \"Succeeded\"\r\n or MachineStatus != \"Connected\"\r\n| extend FailureReasons = strcat( // Makes a String to list all the reason that this resource got flagged for\r\n iif(MachineStatus != \"Connected\",strcat(\"- Machine's status is \", MachineStatus,\" -\"),\"\") ,\r\n iif(MachineErrors != \"[]\",\"- Machine reports errors -\", \"\"),\r\n iif(properties.instanceView.status.message !contains \"SQL Server Extension Agent: Healthy\",\"- Extension reported unhealthy -\",\"\"), \r\n iif(isNotInDateRange,\"- Last upload outside acceptable range -\",\"\"),\r\n iif(properties.instanceView.status.message !contains \"uploadStatus : OK\",\"- Upload status is not reported OK -\",\"\"), \r\n iif(properties.provisioningState != \"Succeeded\",strcat(\"- Extension provisiong state is \", properties.provisioningState,\" -\"),\"\") \r\n )\r\n| extend RecommendedAction = //Attempt to Identify RootCause based on information gathered, and point customer to what they should investigate first.\r\n iif(MachineStatus == \"Disconnected\", \"Machine is disconnected. Please reconnect the machine.\",\r\n iif(MachineStatus == \"Expired\", \"Machine cert is expired. Go to the machine on the Azure Portal for more information on how to resolve this issue.\",\r\n iif(MachineStatus != \"Connected\", strcat(\"Machine status is \", MachineStatus,\". Investigate and resolve this issue.\"),\r\n iif(MachineProvisioningStatus != \"Succeeded\", strcat(\"Machine provisioning status is \", MachineProvisioningStatus, \". Investigate and resolve machine provisioning status\"),\r\n iff(MachineErrors != \"[]\", \"Machine is reporting errors. Investigate and resolve machine errors\",\r\n iif(properties.provisioningState != \"Succeeded\", strcat(\"Extension provisioning status is \", properties.provisioningState,\". Investigate and resolve extension provisioning state.\"),\r\n iff(properties.instanceView.status.message !contains \"SQL Server Extension Agent:\" and properties.instanceView.status.message contains \"SQL Server Extension Agent Deployer\", \"SQL Server extension eeployer ran. However, SQL Server extension seems to not be running. Verify that the extension is currently running.\",\r\n iff(properties.instanceView.status.message !contains \"uploadStatus : OK\" or isNotInDateRange or properties.instanceView.status.message !contains \"SQL Server Extension Agent: Healthy\", \"Extension reported as unhealthy. View FailureReasons and LastExtensionStatusMessage for more information as to the cause of the failure.\",\r\n \"Unable to reccommend actions. Please view FailureReasons.\"\r\n )\r\n )\r\n )\r\n )\r\n )\r\n )\r\n )\r\n )\r\n| project ID = id, MachineName, ResourceGroup = resourceGroup, SubscriptionID = subscriptionId, Location = location, RecommendedAction, FailureReasons, LicenseType = properties.settings.LicenseType, \r\n LastReportedExtensionHealth = iif(properties.instanceView.status.message !contains \"SQL Server Extension Agent: Healthy\", \"Unhealthy\", \"Healthy\"),\r\n LastExtensionUploadTimestamp = iif(indexof(properties.instanceView.status.message, \"timestampUTC : \") > 0,\r\n substring(properties.instanceView.status.message, indexof(properties.instanceView.status.message, \"timestampUTC : \") + 15, 10),\r\n \"no timestamp\"),\r\n LastExtensionUploadStatus = iif(indexof(properties.instanceView.status.message, \"uploadStatus : OK\") > 0, \"OK\", \"Unhealthy\"),\r\n ExtensionProvisioningState = properties.provisioningState,\r\n MachineStatus, MachineErrors, MachineProvisioningStatus,MachineId = machineId,\r\n LastExtensionStatusMessage = properties.instanceView.status.message",
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryGridTile",
+ "settings": {},
+ "partHeader": {
+ "title": "Arc-enabled SQL Server Health",
+ "subtitle": ""
+ }
+ }
+ }
+ ]
+ }
+ ],
+ "metadata": {
+ "model": {
+ "timeRange": {
+ "value": {
+ "relative": {
+ "duration": 24,
+ "timeUnit": 1
+ }
+ },
+ "type": "MsPortalFx.Composition.Configuration.ValueTypes.TimeRange"
+ }
+ }
+ }
+ },
+ "name": "Arc-enabled SQL Server Health",
+ "type": "Microsoft.Portal/dashboards",
+ "location": "INSERT LOCATION",
+ "tags": {
+ "hidden-title": "Arc-enabled SQL Server Health"
+ },
+ "apiVersion": "2022-12-01-preview"
+}
\ No newline at end of file
diff --git a/samples/features/azure-arc/dashboard/SQL Server Estate Health.json b/samples/features/azure-arc/dashboard/SQL Server Estate Health.json
new file mode 100644
index 0000000000..f0fcb93e33
--- /dev/null
+++ b/samples/features/azure-arc/dashboard/SQL Server Estate Health.json
@@ -0,0 +1,874 @@
+{
+ "properties": {
+ "lenses": [
+ {
+ "order": 0,
+ "parts": [
+ {
+ "position": {
+ "x": 0,
+ "y": 0,
+ "colSpan": 2,
+ "rowSpan": 3
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "chartType",
+ "isOptional": true
+ },
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "// Count of all resources\nresources\n| where type == \"microsoft.sql/managedinstances\" or \n type == \"microsoft.azurearcdata/sqlserverinstances\" or \n type == \"microsoft.sql/servers\" or \n type==\"microsoft.sqlvirtualmachine/sqlvirtualmachines\"\n| summarize Servers=count()\n",
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQuerySingleValueTile",
+ "settings": {},
+ "partHeader": {
+ "title": "Number of Servers",
+ "subtitle": ""
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 2,
+ "y": 0,
+ "colSpan": 6,
+ "rowSpan": 4
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "chartType",
+ "value": 2,
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\n| where type == \"microsoft.sql/managedinstances\" or \n type == \"microsoft.azurearcdata/sqlserverinstances\" or \n type == \"microsoft.sql/servers\" or \n type == \"microsoft.sqlvirtualmachine/sqlvirtualmachines\"\n| summarize ResourceCount=count() by iff( \n type==\"microsoft.sql/managedinstances\",\"Azure SQL Manage Instance\"\n ,iif(\n type==\"microsoft.azurearcdata/sqlserverinstances\",\"Arc Enable SQL Server\"\n ,iif(\n type==\"microsoft.sql/servers\",\"Azure SQL DB\"\n ,iif(\n type==\"microsoft.sqlvirtualmachine/sqlvirtualmachines\",\"Azure SQL Server On VM\",\"Not Found\"\n )\n )\n )\n )\n",
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryChartTile",
+ "settings": {},
+ "partHeader": {
+ "title": "Sql Server Type Distributions",
+ "subtitle": ""
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 8,
+ "y": 0,
+ "colSpan": 6,
+ "rowSpan": 4
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "chartType",
+ "value": 2,
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "// top ten resource types by number of resources\n// Count of all resources\nresources\n| where (type == \"microsoft.sql/managedinstances/databases\" or \n type == \"microsoft.azurearcdata/sqlserverinstances/databases\" or \n type == \"microsoft.sql/servers/databases\" or \n type==\"microsoft.sqlvirtualmachine/sqlvirtualmachines/databases\") and \n (name !in (\"master\",\"model\",\"msdb\") and \n name !contains \"tempdb\")\n| summarize ResourceCount=count() by iff( \n type==\"microsoft.sql/managedinstances/databases\",\"Azure SQL Manage Instance\"\n ,iif(\n type==\"microsoft.azurearcdata/sqlserverinstances/databases\",\"Arc Enable SQL Server\"\n ,iif(\n type==\"microsoft.sql/servers/databases\",\"Azure SQL DB\"\n ,iif(\n type==\"microsoft.sqlvirtualmachine/sqlvirtualmachines/databases\",\"Azure SQL Server On VM\",\"Not Found\"\n )\n )\n )\n )",
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryChartTile",
+ "settings": {},
+ "partHeader": {
+ "title": "User Databases per Type",
+ "subtitle": "Does Not Include System Databases"
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 14,
+ "y": 0,
+ "colSpan": 6,
+ "rowSpan": 4
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ },
+ {
+ "name": "chartType",
+ "value": 2,
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\r\n| where type == \"microsoft.sql/managedinstances\" or \r\n type == \"microsoft.azurearcdata/sqlserverinstances\" or \r\n type == \"microsoft.sql/servers\" or \r\n type == \"microsoft.sqlvirtualmachine/sqlvirtualmachines\"\r\n| summarize Count=sum(toint(properties['vCore'])) by tostring(properties['edition'])\r\n| where isnotempty (properties_edition)\r\n| order by Count desc",
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryChartTile",
+ "settings": {},
+ "partHeader": {
+ "title": "Core Count by Edition",
+ "subtitle": ""
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 0,
+ "y": 3,
+ "colSpan": 2,
+ "rowSpan": 6
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "chartType",
+ "isOptional": true
+ },
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "// Count of all resources\nresources\n| where (type == \"microsoft.sql/managedinstances/databases\" or \n type == \"microsoft.azurearcdata/sqlserverinstances/databases\" or \n type == \"microsoft.sql/servers/databases\" )\n| count ",
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQuerySingleValueTile",
+ "settings": {},
+ "partHeader": {
+ "title": "Number of Databases",
+ "subtitle": "Includes all system databases"
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 2,
+ "y": 4,
+ "colSpan": 6,
+ "rowSpan": 5
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 2",
+ "isOptional": true
+ },
+ {
+ "name": "chartType",
+ "value": 1,
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "// If distinct count is small (e.g. < 1000)\n// run next query to get count of each value\nresources\n| where type == \"microsoft.sql/managedinstances\" or \n type == \"microsoft.azurearcdata/sqlserverinstances\" or \n type == \"microsoft.sqlvirtualmachine/sqlvirtualmachines\"\n| summarize Count=count() by iif(type==\"microsoft.azurearcdata/sqlserverinstances\"\n,tostring(properties['edition'])\n, iif(type==\"microsoft.sql/managedinstances\", tostring(sku['tier'])\n,tostring(properties['sqlImageSku'])))\n| order by Count desc",
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryChartTile",
+ "settings": {},
+ "partHeader": {
+ "title": "Servers per SQL Server Editions",
+ "subtitle": ""
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 8,
+ "y": 4,
+ "colSpan": 6,
+ "rowSpan": 5
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "chartType",
+ "value": 1,
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\r\n| where type == \"microsoft.azurearcdata/sqlserverinstances\"\r\n| summarize Count=sum(toint(properties['vCore'])) by tostring(properties['licenseType'])\r\n| order by Count desc",
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryChartTile",
+ "settings": {},
+ "partHeader": {
+ "title": "Cores Per LicenseType",
+ "subtitle": ""
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 14,
+ "y": 4,
+ "colSpan": 6,
+ "rowSpan": 5
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\n| where type == \"microsoft.azurearcdata/sqlserverinstances/databases\"\n| extend prop_db=parse_xml(properties) \n| extend Recovery_Model = prop_db.recoveryMode\n| summarize Count = count() by tostring(Recovery_Model)",
+ "isOptional": true
+ },
+ {
+ "name": "chartType",
+ "value": 1,
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryChartTile",
+ "settings": {},
+ "partHeader": {
+ "title": "SQL Server Recovery Mode",
+ "subtitle": ""
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 0,
+ "y": 9,
+ "colSpan": 6,
+ "rowSpan": 6
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 2",
+ "isOptional": true
+ },
+ {
+ "name": "chartType",
+ "value": 1,
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\n| where type == \"microsoft.azurearcdata/sqlserverinstances\"\n| summarize Count=count() by iff(tostring(properties['azureDefenderStatus'])==\"Unknown\", \"Not Protected\",tostring(properties['azureDefenderStatus']))\n| order by Count desc",
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryChartTile",
+ "settings": {},
+ "partHeader": {
+ "title": "Defender for SQL",
+ "subtitle": ""
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 6,
+ "y": 9,
+ "colSpan": 6,
+ "rowSpan": 6
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 2",
+ "isOptional": true
+ },
+ {
+ "name": "chartType",
+ "value": 1,
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "// If distinct count is small (e.g. < 1000)\n// run next query to get count of each value\nresources\n| where type == \"microsoft.azurearcdata/sqlserverinstances/databases\"\n|summarize Count=count() by tostring(properties['compatibilityLevel'])\n| order by Count desc",
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryChartTile",
+ "settings": {},
+ "partHeader": {
+ "title": "Compatibility Mode",
+ "subtitle": ""
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 12,
+ "y": 9,
+ "colSpan": 7,
+ "rowSpan": 6
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\n| where type == \"microsoft.azurearcdata/sqlserverinstances\"\n| summarize Count=count() by tostring(properties['version'])",
+ "isOptional": true
+ },
+ {
+ "name": "chartType",
+ "value": 1,
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryChartTile",
+ "settings": {},
+ "partHeader": {
+ "title": "SQL Server Versions",
+ "subtitle": ""
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 0,
+ "y": 15,
+ "colSpan": 6,
+ "rowSpan": 5
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ },
+ {
+ "name": "chartType",
+ "value": 2,
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\n| where (type == \"microsoft.sql/managedinstances/databases\" or \n type == \"microsoft.azurearcdata/sqlserverinstances/databases\" or \n type == \"microsoft.sql/servers/databases\" )\n| extend prop_db=parse_xml(properties) \n| extend prop_db_recoveryMode = prop_db.recoveryMode\n| extend prop_db_databaseCreationDate = todatetime(prop_db.databaseCreationDate)\n| extend prop_db_backupInformation = prop_db.backupInformation\n| extend prop_db_backupInformation_lastFullBackup = todatetime(prop_db.backupInformation.lastFullBackup)\n| extend calc_lastBackupAgeDays = iff(isnull(prop_db_backupInformation_lastFullBackup),\n datetime_diff('Day',now(),prop_db_databaseCreationDate),\n datetime_diff('Day',now(),prop_db_backupInformation_lastFullBackup)\n )\n| extend Backup_1day = iif( calc_lastBackupAgeDays <=1 , 1 , 0)\n| extend Backup_7day = iif( calc_lastBackupAgeDays >1 and calc_lastBackupAgeDays <=7 , 1 , 0)\n| extend Backup_over7day = iif( calc_lastBackupAgeDays >7 , 1 , 0)\n| extend Backup_desc = case(\n isnull(prop_db_backupInformation_lastFullBackup), \"no backups at all\", \n datetime_diff('Day',now(),prop_db_backupInformation_lastFullBackup) <= 1, \"1 day\", \n datetime_diff('Day',now(),prop_db_backupInformation_lastFullBackup) <= 7, \"7 days\", \n datetime_diff('Day',now(),prop_db_backupInformation_lastFullBackup) <= 30, \"1 Month\", \n \"older than a month\")\n| project database_name = name, RecoveryModel = prop_db_recoveryMode, Last_Full_Backup = prop_db_backupInformation_lastFullBackup , Backup_Age_Days = calc_lastBackupAgeDays ,Backup_1day ,Backup_7day ,Backup_over7day, Backup_desc\n| summarize Count=count() by Backup_desc",
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryChartTile",
+ "settings": {},
+ "partHeader": {
+ "title": "SQL Server Backups Intervals",
+ "subtitle": ""
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 6,
+ "y": 15,
+ "colSpan": 6,
+ "rowSpan": 5
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "chartType",
+ "value": 1,
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\n| where ['type'] ==\"microsoft.azurearcdata/sqlserverinstances/databases\"\n| summarize Count=count() by Encrypte=iif(tostring(properties['databaseOptions'].isEncrypted)==\"false\",\"No\", \"Yes\")",
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryChartTile",
+ "settings": {},
+ "partHeader": {
+ "title": "Encrypted",
+ "subtitle": ""
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 12,
+ "y": 15,
+ "colSpan": 6,
+ "rowSpan": 5
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "chartType",
+ "isOptional": true
+ },
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\r\n | where type =~ 'microsoft.hybridcompute/machines/extensions'\r\n | where properties.type in ('WindowsAgent.SqlServer','LinuxAgent.SqlServer')\r\n | parse id with * '/providers/Microsoft.HybridCompute/machines/' machineName '/extensions/' *\r\n | parse properties with * 'uploadStatus : ' uploadStatus ';' *\r\n | project machineName, uploadStatus, subscriptionId, resourceGroup\r\n //| where uploadStatus !in ('OK') //comment this out to see all upload stats\r\n | order by uploadStatus desc",
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryGridTile",
+ "settings": {},
+ "partHeader": {
+ "title": "SQL Extension Upload Status",
+ "subtitle": ""
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 0,
+ "y": 20,
+ "colSpan": 20,
+ "rowSpan": 4
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "chartType",
+ "isOptional": true
+ },
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\r\n| where type == \"microsoft.hybridcompute/machines\"\r\n| where properties.detectedProperties.mssqldiscovered == \"true\"\r\n| extend machineIdHasSQLServerDiscovered = id\r\n| project name, machineIdHasSQLServerDiscovered, resourceGroup, subscriptionId\r\n| join kind= leftouter (\r\n resources\r\n | where type == \"microsoft.hybridcompute/machines/extensions\"\r\n | where properties.type in (\"WindowsAgent.SqlServer\",\"LinuxAgent.SqlServer\")\r\n | extend machineIdHasSQLServerExtensionInstalled = iff(id contains \"/extensions/WindowsAgent.SqlServer\" or id contains \"/extensions/LinuxAgent.SqlServer\", substring(id, 0, indexof(id, \"/extensions/\")), \"\")\r\n | project Provisioning_State = properties.provisioningState,\r\n License_Type = properties.settings.LicenseType,\r\n ESU = iff(notnull(properties.settings.enableExtendedSecurityUpdates), \"enabled\", \"\"),\r\n Extension_Version = properties.instanceView.typeHandlerVersion,\r\n Exlcuded_instaces = properties.ExcludedSqlInstances,\r\n Purview = iff(notnull(properties.settings.ExternalPolicyBasedAuthorization),\"enabled\",\"\"),\r\n Entra = iff(notnull(properties.settings.AzureAD),\"enabled\",\"\"),\r\n BPA = iff(notnull(properties.settings.AssessmentSettings),\"enabled\",\"\"),\r\n machineIdHasSQLServerExtensionInstalled)\r\non $left.machineIdHasSQLServerDiscovered == $right.machineIdHasSQLServerExtensionInstalled\r\n| where isnotempty(machineIdHasSQLServerExtensionInstalled)\r\n| project-away machineIdHasSQLServerDiscovered, machineIdHasSQLServerExtensionInstalled",
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryGridTile",
+ "settings": {},
+ "partHeader": {
+ "title": "Arc SQL details",
+ "subtitle": ""
+ }
+ }
+ },
+ {
+ "position": {
+ "x": 0,
+ "y": 24,
+ "colSpan": 20,
+ "rowSpan": 4
+ },
+ "metadata": {
+ "inputs": [
+ {
+ "name": "chartType",
+ "isOptional": true
+ },
+ {
+ "name": "isShared",
+ "isOptional": true
+ },
+ {
+ "name": "queryId",
+ "isOptional": true
+ },
+ {
+ "name": "formatResults",
+ "isOptional": true
+ },
+ {
+ "name": "partTitle",
+ "value": "Query 1",
+ "isOptional": true
+ },
+ {
+ "name": "queryScope",
+ "value": {
+ "scope": 0,
+ "values": []
+ },
+ "isOptional": true
+ },
+ {
+ "name": "query",
+ "value": "resources\r\n| where (type == \"microsoft.sql/managedinstances/databases\" or \r\n type == \"microsoft.azurearcdata/sqlserverinstances/databases\" or \r\n type == \"microsoft.sql/servers/databases\" ) and \r\n (name !in (\"master\",\"model\",\"msdb\") and \r\n name !contains \"tempdb\")\r\n| summarize Dbs=count(),Offline=sum(toint(iif(tostring(properties[\"state\"])!=\"Online\" and tostring(properties[\"status\"])!=\"Online\" and tostring(properties[\"status\"])!=\"Paused\" ,1,0)))\r\n,SizeMB=sum(toint(iif(tostring(properties[\"sizeMB\"])!=\"\",properties[\"sizeMB\"],0)))\r\n,Type=max(\r\n iff( type==\"microsoft.sql/managedinstances/databases\",\"Azure SQL Manage Instance\"\r\n ,iif(type==\"microsoft.azurearcdata/sqlserverinstances/databases\",\"Arc Enable SQL Server\"\r\n ,iif(type==\"microsoft.sql/servers/databases\",\"Azure SQL DB\"\r\n ,iif(type==\"microsoft.sqlvirtualmachine/sqlvirtualmachines/databases\",\"Azure SQL Server On VM\",\"Not Found\")\r\n )\r\n )\r\n )\r\n ) by Instances = tostring(split(tostring(id),\"/\")[8])\r\n| order by Offline",
+ "isOptional": true
+ }
+ ],
+ "type": "Extension/HubsExtension/PartType/ArgQueryGridTile",
+ "settings": {},
+ "partHeader": {
+ "title": "Dbs per Servers",
+ "subtitle": ""
+ }
+ }
+ }
+ ]
+ }
+ ],
+ "metadata": {
+ "model": {
+ "timeRange": {
+ "value": {
+ "relative": {
+ "duration": 24,
+ "timeUnit": 1
+ }
+ },
+ "type": "MsPortalFx.Composition.Configuration.ValueTypes.TimeRange"
+ }
+ }
+ }
+ },
+ "name": "SQL Data Estate Health",
+ "type": "Microsoft.Portal/dashboards",
+ "location": "INSERT LOCATION",
+ "tags": {
+ "hidden-title": "SQL Data Estate Health"
+ },
+ "apiVersion": "2022-12-01-preview"
+}
\ No newline at end of file
diff --git a/samples/features/azure-arc/dashboard/SQL Server Instances.json b/samples/features/azure-arc/dashboard/SQL Server Instances.json
index 886cc07157..17e8b267e2 100644
--- a/samples/features/azure-arc/dashboard/SQL Server Instances.json
+++ b/samples/features/azure-arc/dashboard/SQL Server Instances.json
@@ -1,10 +1,10 @@
{
"properties": {
- "lenses": {
- "0": {
+ "lenses": [
+ {
"order": 0,
- "parts": {
- "0": {
+ "parts": [
+ {
"position": {
"x": 0,
"y": 0,
@@ -56,7 +56,7 @@
}
}
},
- "1": {
+ {
"position": {
"x": 3,
"y": 0,
@@ -109,7 +109,7 @@
}
}
},
- "2": {
+ {
"position": {
"x": 9,
"y": 0,
@@ -161,7 +161,7 @@
}
}
},
- "3": {
+ {
"position": {
"x": 12,
"y": 0,
@@ -214,7 +214,7 @@
}
}
},
- "4": {
+ {
"position": {
"x": 0,
"y": 5,
@@ -266,7 +266,7 @@
}
}
},
- "5": {
+ {
"position": {
"x": 0,
"y": 9,
@@ -318,7 +318,7 @@
}
}
},
- "6": {
+ {
"position": {
"x": 0,
"y": 13,
@@ -371,7 +371,7 @@
}
}
},
- "7": {
+ {
"position": {
"x": 6,
"y": 13,
@@ -424,7 +424,7 @@
}
}
},
- "8": {
+ {
"position": {
"x": 13,
"y": 13,
@@ -477,7 +477,7 @@
}
}
},
- "9": {
+ {
"position": {
"x": 0,
"y": 19,
@@ -530,7 +530,7 @@
}
}
},
- "10": {
+ {
"position": {
"x": 6,
"y": 19,
@@ -583,7 +583,7 @@
}
}
},
- "11": {
+ {
"position": {
"x": 12,
"y": 19,
@@ -636,7 +636,7 @@
}
}
},
- "12": {
+ {
"position": {
"x": 0,
"y": 25,
@@ -689,7 +689,7 @@
}
}
},
- "13": {
+ {
"position": {
"x": 6,
"y": 25,
@@ -742,7 +742,7 @@
}
}
},
- "14": {
+ {
"position": {
"x": 0,
"y": 30,
@@ -796,9 +796,9 @@
}
}
}
- }
+ ]
}
- },
+ ],
"metadata": {
"model": {
"timeRange": {
@@ -819,5 +819,5 @@
"tags": {
"hidden-title": "SQL Server Instances"
},
- "apiVersion": "2015-08-01-preview"
+ "apiVersion": "2022-12-01-preview"
}
diff --git a/samples/features/azure-arc/troubleshooting/hybrid-compute-extension-last-connect.kql b/samples/features/azure-arc/troubleshooting/hybrid-compute-extension-last-connect.kql
new file mode 100644
index 0000000000..6536813a03
--- /dev/null
+++ b/samples/features/azure-arc/troubleshooting/hybrid-compute-extension-last-connect.kql
@@ -0,0 +1,7 @@
+resources
+ | where type =~ 'microsoft.hybridcompute/machines/extensions'
+ | where properties.type in ('WindowsAgent.SqlServer','LinuxAgent.SqlServer')
+ | parse id with * '/providers/Microsoft.HybridCompute/machines/' machineName '/extensions/' *
+ | parse properties with * 'timestampUTC : ' timestampUTC ';' *
+ | project timestampUTC, subscriptionId, resourceGroup, machineName
+ | order by timestampUTC desc
\ No newline at end of file
diff --git a/samples/features/azure-arc/troubleshooting/test-connectivity.ps1 b/samples/features/azure-arc/troubleshooting/test-connectivity.ps1
new file mode 100644
index 0000000000..965990923a
--- /dev/null
+++ b/samples/features/azure-arc/troubleshooting/test-connectivity.ps1
@@ -0,0 +1,70 @@
+#This script repeatedly probes all regions for connectivity to the Azure Arc data services/Arc-enabled SQL Server endpoints for telemetry and the data processing service.
+#The script will output the status of the connectivity to the console.
+#The script will run indefinitely until stopped by the user.
+#The script will iterate through all regions in the $regions array.
+#The list of regions are updated as of June 7,2024 to reflect all publicly available, supported Azure regions for Arc-enabled SQL Server.
+
+$regions = @(
+ "East US",
+ "East US 2",
+ "West US 2",
+ "West US 3",
+ "Central US",
+ "North Central US",
+ "South Central US",
+ "West Central US",
+ "Canada Central",
+ "Canada East",
+ "UK South",
+ "UK West",
+ "France Central",
+ "West Europe",
+ "North Europe",
+ "Switzerland North",
+ "Central India",
+ "Brazil South",
+ "South Africa North",
+ "UAE North",
+ "Japan East",
+ "Korea Central",
+ "Southeast Asia",
+ "Australia East",
+ "Sweden Central",
+ "Norway East"
+)
+
+$regions = $regions | ForEach-Object { $_.Replace(" ", "") }
+
+do{
+ $regions | ForEach-Object {
+ $dps_url = "dataprocessingservice.$_.arcdataservices.com"
+ $ti_url = "telemetry.$_.arcdataservices.com"
+ try{
+ $dps_response_time = Measure-Command { $response = Invoke-WebRequest -Uri $dps_url -Method Get }
+ $dps_result = ($response).StatusCode
+ }catch{
+ $dps_result = $_.Exception.Message
+ }
+ try{
+ $ti_response_time = Measure-Command { $response = Invoke-WebRequest -Uri $ti_url -Method Get -SkipHttpErrorCheck }
+ }catch{
+ if($_.Exception.Message -like "*401*"){
+ $ti_result = "Expected"
+ }
+ else {
+ $ti_result = $_.Exception.Message
+ }
+ }
+ if ($ti_response_time.TotalSeconds -gt 3 -or $dps_response_time.TotalSeconds -gt 3 -or $dps_result -ne 200 -or $ti_result -ne "Expected") {
+ Write-Host $dps_result "($dps_response_time) " $ti_result " ($ti_response_time) :: $_" -ForegroundColor Red
+ }
+ elseif ($ti_response_time.TotalSeconds -gt 1 -or $dps_response_time.TotalSeconds -gt 1) {
+ Write-Host $dps_result "($dps_response_time) " $ti_result " ($ti_response_time) :: $_" -ForegroundColor Yellow
+ }
+ else
+ {
+ Write-Host $dps_result "($dps_response_time) " $ti_result " ($ti_response_time) :: $_"
+ }
+ }
+ Write-Host "====================================================================="
+} while($true)
\ No newline at end of file
diff --git a/samples/features/reporting-services/ssrs-migration-rss/ssrs_migration.rss b/samples/features/reporting-services/ssrs-migration-rss/ssrs_migration.rss
index ac67503709..e18ad390b5 100644
--- a/samples/features/reporting-services/ssrs-migration-rss/ssrs_migration.rss
+++ b/samples/features/reporting-services/ssrs-migration-rss/ssrs_migration.rss
@@ -16,19 +16,19 @@
' 1) Download ssrs_migration.rss
' 2) Open a command prompt and navigate to the folder containing ssrs_migration.rss, for example c:\rss
' 3) Run the following command (in one line):
-' rs.exe -i ssrs_migration.rss -e Mgmt2010
+' rs.exe -i ssrs_migration.rss -e Mgmt2010
'
-' -s SOURCE_URL 'URL of the source RS server.
-' -u domain\username -p password 'Credentials for source server. OPTIONAL, default credentials are used if missing.
-' -v st="SITE" 'Specifies SharePoint site, in case source server is in SharePoint integrated mode
-' -v f="SOURCEFOLDER" 'Set to "/" for migrating everything, or to something like "/folder/subfolder" for partial migration. Everything within this folder will be copied. OPTIONAL, default is "/".
+' -s SOURCE_URL 'URL of the source RS server.
+' -u domain\username -p password 'Credentials for source server. OPTIONAL, default credentials are used if missing.
+' -v st="SITE" 'Specifies SharePoint site, in case source server is in SharePoint integrated mode
+' -v f="SOURCEFOLDER" 'Set to "/" for migrating everything, or to something like "/folder/subfolder" for partial migration. Everything within this folder will be copied. OPTIONAL, default is "/".
'
-' -v ts="TARGET_URL" 'URL of the target RS server"
-' -v tu="domain\username" -v tp="password" 'Credentials for target server. OPTIONAL, default credentials are used if missing.
-' -v tst="SITE" 'Specifies SharePoint site, in case target server is in SharePoint integrated mode
-' -v tf="TARGETFOLDER" 'Set to "/" for migrating into the root level, or to something like "/folder/subfolder" for copying into some folder, which must be already existing. Everything within "SOURCEFOLDER" will be copied into "TARGETFOLDER". OPTIONAL, default is "/".
-' -v security= "True/False" 'If set to "False", destination catalog items will inherit security setting according to the settings of the target system. Default is false.
-' -v logprefix="PREFIX" 'Prefix name to the output log file.
+' -v ts="TARGET_URL" 'URL of the target RS server"
+' -v tu="domain\username" -v tp="password" 'Credentials for target server. OPTIONAL, default credentials are used if missing.
+' -v tst="SITE" 'Specifies SharePoint site, in case target server is in SharePoint integrated mode
+' -v tf="TARGETFOLDER" 'Set to "/" for migrating into the root level, or to something like "/folder/subfolder" for copying into some folder, which must be already existing. Everything within "SOURCEFOLDER" will be copied into "TARGETFOLDER". OPTIONAL, default is "/".
+' -v security= "True/False" 'If set to "False", destination catalog items will inherit security setting according to the settings of the target system. Default is false.
+' -v logprefix="PREFIX" 'Prefix name to the output log file (default is MigrationLog.csv created from where script is run).
'
' Example: rs.exe -i ssrs_migration.rss -e Mgmt2010 -s http://server1/reportserver -v ts="http://server2/_vti_bin/reportserver"
' Example: rs.exe -i ssrs_migration.rss -e Mgmt2010 -s http://server1/reportserver -u domain1\user1 -p password1 -v f="/SOURCEFOLDER" -v ts="http://server2/_vti_bin/reportserver" -v tu="domain1\user2" -v tp="password2" -v tf="/TARGETFOLDER"
@@ -75,7 +75,7 @@ Private policies As Policy() = Nothing
Private srcSiteUrl As String = Nothing
Private snkSiteUrl As String = Nothing
-Private logFilePath As String = "MigrationLog.txt"
+Private logFilePath As String = "MigrationLog.csv"
''''''''''''''''''''''''''''''''''''''''''''''
Sub Main()
@@ -241,8 +241,8 @@ Sub Main()
RsSnk.CreateFolder(folderName, parentFolder, Nothing)
Catch er As Exception
If (er.Message.Contains("Microsoft.ReportingServices.Diagnostics.Utilities.ItemAlreadyExistsException"))
- Console.ForegroundColor = ConsoleColor.Yellow
- Console.WriteLine("Folder already exists.")
+ Console.ForegroundColor = ConsoleColor.Gray
+ Console.WriteLine("Folder already exists: " + folderName)
Console.ResetColor()
Else
Throw ' Unknown Exception
@@ -478,7 +478,7 @@ Function MigrateDataSource(srcItem As CatalogItem) As CatalogItem
Console.WriteLine(warningMsg)
Console.ResetColor()
- LogErrorToFile("CATALOGITEM", "Warning", warningMsg)
+ LogErrorToFile("CATALOGITEM", "Warning: " + result.Path, warningMsg)
End If
@@ -567,7 +567,7 @@ Function MigrateCatalogItem(srcItem As CatalogItem) As CatalogItem
Console.WriteLine(warningMsg)
Console.ResetColor()
- LogErrorToFile("CATALOGITEM", "Warning", warningMsg)
+ LogErrorToFile("CATALOGITEM", "Warning: " + result.TypeName, warningMsg)
End If
End If
Next
@@ -579,6 +579,7 @@ Function MigrateCatalogItem(srcItem As CatalogItem) As CatalogItem
For Each w As Warning In warnings
If w.Code <> "rsDataSourceReferenceNotPublished" And w.Code <> "rsDataSetReferenceNotPublished" Then 'This should be fine after re-linking
Console.WriteLine("Warning: " + w.Message)
+ LogErrorToFile("CATALOGITEM", "Warning: " + srcItem.Path, w.Message)
End If
Next
Console.ResetColor()
@@ -742,7 +743,7 @@ Sub MigrateReportSubscriptions(snkReport As CatalogItem, srcReport As CatalogIte
Console.Write(description)
If Not dataRetrievalPlan Is Nothing And TypeOf dataRetrievalPlan.Item Is DataSourceReference Then
Dim item As DataSourceReference = dataRetrievalPlan.Item
- Dim ref As String = GetSnkPath(item.Reference)
+ Dim ref As String = GetSnkPathRef(item.Reference)
item.Reference = ref
End If
RsSnk.CreateDataDrivenSubscription(snkReport.Path, extensionSettings, dataRetrievalPlan, description, eventType, GetMatchData(matchData, RsSnk), parameters)
@@ -756,7 +757,7 @@ Sub MigrateReportSubscriptions(snkReport As CatalogItem, srcReport As CatalogIte
Console.WriteLine(warningMsg)
Console.ResetColor()
- LogErrorToFile("SUBSCRIPTIONS", "Warning", warningMsg)
+ LogErrorToFile("SUBSCRIPTIONS", "Warning: " + snkReport.Path, warningMsg)
End If
End If
@@ -795,9 +796,9 @@ Sub MigrateReportSubscriptions(snkReport As CatalogItem, srcReport As CatalogIte
Console.WriteLine(warningMsg)
Console.ResetColor()
- LogErrorToFile("SUBSCRIPTIONS", "Warning", warningMsg)
+ LogErrorToFile("SUBSCRIPTIONS", "Warning: " + snkReport.Path, warningMsg)
- End If
+ End If
Next
Catch ex As Exception
@@ -990,10 +991,19 @@ End Sub
'Helper function to link snk reference with relative paths
Function GetSnkPathRef(srcPath As String) As String
- Dim snkPath = srcPath
- If srcSiteUrl isNot Nothing
- snkPath = srcPath.Remove(0, srcSiteUrl.LastIndexOf("/"))
- End If
+ Dim snkPath = srcPath
+
+ If Not SrcIsNative Then
+ 'SharePoint integrated mode, will assume /sites server relative URL delimeter
+ If srcSiteUrl isNot Nothing
+ Dim delimeter = "/sites/"
+ snkPath = srcPath.Remove(0, srcSiteUrl.IndexOf(delimeter) + delimeter.Length-1)
+ End If
+ Else
+ 'Treat path reference like other scenarios if not intergated mode
+ snkPath = GetSnkPath(srcPath)
+ End If
+
Return snkPath
End Function
@@ -1006,7 +1016,11 @@ Function GetSnkPath(srcPath As String) As String
Dim snkPath As String = SnkFolder
If Not srcPath = "" Then
- snkPath = snkPath + IIf(SrcFolder = "/", srcPath, srcPath.Remove(0, SrcFolder.Length))
+ If SrcFolder = SnkFolder Then ' exact relative folder paths assumed between source and target
+ snkPath = srcPath
+ Else
+ snkPath = snkPath + IIf(SrcFolder = "/", srcPath, srcPath.Remove(0, SrcFolder.Length))
+ End If
End If
If snkPath.StartsWith("//") Then
@@ -1189,12 +1203,18 @@ Sub LogErrorToFile(source As String, errItem as String, message As String)
Dim writer as StreamWriter
Try
+ Dim fileExists As Boolean = Not File.Exists(logFilePath) OrElse New FileInfo(logFilePath).Length = 0
+
' Create a StreamWriter object
writer = New StreamWriter(logFilePath, True)
+ If fileExists Then
+ writer.WriteLine("DateTime, Source, ErrorItem, ErrorMessage")
+ End If
+
' Format log message with timestamp
Dim timeStamp As String = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
- Dim formattedMessage as String = String.Format("{0}, {1}, {2}, ""{3}""", timeStamp, source, errItem, message.Replace(Environment.NewLine, ""))
+ Dim formattedMessage as String = String.Format("{0}, {1}, {2}, ""{3}""", timeStamp, source, errItem, message.Replace(Environment.NewLine, " "))
' Write the log entry to file
writer.WriteLine(formattedMessage)
diff --git a/samples/features/security/always-encrypted-with-secure-enclaves/sample-application/AlwaysEncryptedConsole/AlwaysEncryptedConsole.csproj b/samples/features/security/always-encrypted-with-secure-enclaves/sample-application/AlwaysEncryptedConsole/AlwaysEncryptedConsole.csproj
index 8bd770b771..0137486778 100644
--- a/samples/features/security/always-encrypted-with-secure-enclaves/sample-application/AlwaysEncryptedConsole/AlwaysEncryptedConsole.csproj
+++ b/samples/features/security/always-encrypted-with-secure-enclaves/sample-application/AlwaysEncryptedConsole/AlwaysEncryptedConsole.csproj
@@ -11,7 +11,7 @@
-
+
diff --git a/samples/features/security/azure-active-directory-auth/img/app-config-key-value-example.png b/samples/features/security/azure-active-directory-auth/img/app-config-key-value-example.png
deleted file mode 100644
index f44eb0d07b..0000000000
Binary files a/samples/features/security/azure-active-directory-auth/img/app-config-key-value-example.png and /dev/null differ
diff --git a/samples/features/security/azure-active-directory-auth/img/azure-active-directory-application-portal.png b/samples/features/security/azure-active-directory-auth/img/azure-active-directory-application-portal.png
deleted file mode 100644
index bad0f36ae3..0000000000
Binary files a/samples/features/security/azure-active-directory-auth/img/azure-active-directory-application-portal.png and /dev/null differ
diff --git a/samples/features/security/azure-active-directory-auth/img/token-press-any-key-to-stop.png b/samples/features/security/azure-active-directory-auth/img/token-press-any-key-to-stop.png
deleted file mode 100644
index 7bb127d779..0000000000
Binary files a/samples/features/security/azure-active-directory-auth/img/token-press-any-key-to-stop.png and /dev/null differ
diff --git a/samples/features/security/azure-active-directory-auth/img/vs-authentication-method-password.png b/samples/features/security/azure-active-directory-auth/img/vs-authentication-method-password.png
deleted file mode 100644
index b2f7273785..0000000000
Binary files a/samples/features/security/azure-active-directory-auth/img/vs-authentication-method-password.png and /dev/null differ
diff --git a/samples/features/security/azure-active-directory-auth/integrated/README.md b/samples/features/security/azure-active-directory-auth/integrated/README.md
index a6519a0d14..0e66f9c90a 100644
--- a/samples/features/security/azure-active-directory-auth/integrated/README.md
+++ b/samples/features/security/azure-active-directory-auth/integrated/README.md
@@ -1,16 +1,17 @@
## Run this sample
-Note: Run this project on a machine joined to a domain that is federated with Azure Active Directory. A contained database user representing your Azure AD principal, or one of the groups, you belong to, must exist in the database and must have the CONNECT permission.
+Note: Run this project on a machine joined to a domain that is federated with Microsoft Entra. A contained database user representing your Microsoft Entra ID principal, or one of the groups, you belong to, must exist in the database and must have the CONNECT permission.
1. Before building and running the Integrated project:
+ In Program.cs, locate the following lines of code and replace the server/database name with your server/database name.
```
-builder["Data Source"] = "aad-managed-demo.database.windows.net "; // replace 'aad-managed-demo' with your server name
+builder["Data Source"] = ".database.windows.net "; // replace '' with your server name
builder["Initial Catalog"] = "demo"; // replace with your database name
```
-2. The builder["Authentication"] method must be set to SqlAuthenticationMethod.ActiveDirectoryIntegrated;
-![screenshot of visual studio showing builder fields to change] (/samples/features/security/azure-active-directory-auth/img/vs-authentication-method-integrated.png)
+2. The `builder["Authentication"]` method must be set to `SqlAuthenticationMethod.ActiveDirectoryIntegrated`;
-3. Running this project on a machine joined to a domain that is federated with Azure Active Directory will automatically use your Windows credentials and no password is required. The execution window will indicate a successful connection to the database followed by “Please press any key to stop”:
-![screenshot of application after successful authentication- "press any key to stop"] (/samples/features/security/azure-active-directory-auth/img/integrated-press-any-key-to-stop.png)
+ 
+
+3. Running this project on a machine joined to a domain that is federated with Microsoft Entra will automatically use your Windows credentials and no password is required. The execution window will indicate a successful connection to the database followed by “Please press any key to stop”:
+ 
diff --git a/samples/features/security/azure-active-directory-auth/password/README.md b/samples/features/security/azure-active-directory-auth/password/README.md
index 3d890c6a12..23b823429f 100644
--- a/samples/features/security/azure-active-directory-auth/password/README.md
+++ b/samples/features/security/azure-active-directory-auth/password/README.md
@@ -3,20 +3,17 @@
**Before building and running the Password project**:
1. In Program.cs, locate the following lines of code and replace the server/database name with your server/database name.
```
-builder["Data Source"] = "aad-managed-demo.database.windows.net "; // replace 'aad-managed-demo' with your server name
+builder["Data Source"] = ".database.windows.net "; // replace '' with your server name
builder["Initial Catalog"] = "demo"; // replace with your database name
```
-2. Locate the following line of code and replace username, with the name of the Azure AD user you want to connect as.
+2. Locate the following line of code and replace username, with the name of the Microsoft Entra ID user you want to connect as.
```
-string username = "bob@cqclinic.onmicrosoft.com"; // replace with your username
+string username = "bob@contoso.com"; // replace with your username
```
-Note: A contained user database must exist and a contained database user representing the specified Azure AD user or one of the groups, the specified Azure AD user belongs to, must exist in the database and must have the CONNECT permission (except for AAD server admin or group)
+Note: A contained user database must exist and a contained database user representing the specified Microsoft Entra ID user or one of the groups, the specified Microsoft Entra ID user belongs to, must exist in the database and must have the CONNECT permission (except for AAD server admin or group)
-Please note that
-builder["Authentication"] method is set to SqlAuthenticationMethod.ActiveDirectoryPassword.
+Please note that the `builder["Authentication"]` method is set to `SqlAuthenticationMethod.ActiveDirectoryPassword`.
-![screenshot of visual studio showing builder fields to change] (/samples/features/security/azure-active-directory-auth/img/vs-authentication-method-password.png)
+When running this program an execution window a prompt for the Microsoft Entra ID password request for user bob@cqclinic.onmicrosoft.com will appear. Once the password is entered the message should indicate a successful connection to the database followed by “Please press any key to stop”:
-When running this program an execution window a prompt for the Azure AD password request for user bob@cqclinic.onmicrosoft.com will appear. Once the password is entered the message should indicate a successful connection to the database followed by “Please press any key to stop”:
-
-![screenshot of application after successful authentication- "press any key to stop"] (/samples/features/security/azure-active-directory-auth/img/pwd-press-any-key-to-stop.png)
+
diff --git a/samples/features/security/azure-active-directory-auth/readme.md b/samples/features/security/azure-active-directory-auth/readme.md
index e93226d252..48eb99a5a8 100644
--- a/samples/features/security/azure-active-directory-auth/readme.md
+++ b/samples/features/security/azure-active-directory-auth/readme.md
@@ -35,16 +35,15 @@ To run this sample, you need the following prerequisites:
+ ADALSQL.DLL enables applications to authenticate to Microsoft Azure SQL Database using Azure Active Directory. The ADALSQL.DLL is not installed with Visual Studio so download the DLL at http://www.microsoft.com/en-us/download/details.aspx?id=48742
+ ADALSQL.DLL is automatically downloaded with Visual Studio 2015 Update 2, SQL Server Management Studio, and the newest version of SQL Server Data tools
-1. Create Azure Active Directory (AD), or federate your domain with existing Azure AD
- This allows either to use managed or federated accounts associated with a specific Azure AD
-2. Create Azure AD administrator for Azure SQL DB using Azure portal, PowerShell command or Rest API
-3. With help from T-SQL query interface (i.e. SSMS query editor), using Azure AD admin credentials for SQL DB & SQL DW, create an Azure AD user in a designated database. The database user represents your Azure AD principal (or one of the groups you belong to) and must exist in the database having CONNECT permission prior to executing a connection attempt
+1. Create Microsoft Entra tenant (formerly known as Azure Active Directory), or federate your domain with existing Microsoft Entra ID. This allows either to use managed or federated accounts associated with a specific Microsoft Entra ID.
+2. Create Microsoft Entra ID administrator for Azure SQL Database using the Azure portal, PowerShell command, or Rest API.
+3. With help from T-SQL query interface (i.e. SSMS query editor), using Microsoft Entra ID admin credentials for Azure SQL Database and dedicated SQL pools in Azure Synapse, create an Microsoft Entra ID user in a designated database. The database user represents your Microsoft Entra ID principal (or one of the groups you belong to) and must exist in the database having CONNECT permission prior to executing a connection attempt.
**Other Prerequisites**
-1. For Azure AD integrated authentication a computer joined to a domain that is federated with Azure Active Directory is required
-2. An existing database created before a connection attempt is required. The database can be created using credentials for SQL administrator, or Azure AD SQL administrator
+1. For Microsoft Entra ID integrated authentication a computer joined to a domain that is federated with Azure Active Directory is required.
+2. An existing database created before a connection attempt is required. The database can be created using credentials for SQL administrator, or Microsoft Entra SQL administrator.
@@ -62,7 +61,7 @@ To run this sample, you need the following prerequisites:
## Sample details
-This demo provides a simple tool for exploring Azure Active Directory authentication to Azure SQL DB or Azure SQL DW.
+This demo provides a simple tool for exploring Azure Active Directory authentication to Azure SQL Database and dedicated SQL pools in Azure Synapse.
Azure Active Directory authentication with Azure SQL Database V12 supports the following authentication methods:
- User/password authentication
diff --git a/samples/features/security/azure-active-directory-auth/token/README.md b/samples/features/security/azure-active-directory-auth/token/README.md
index 2e44d47194..5a97951df4 100644
--- a/samples/features/security/azure-active-directory-auth/token/README.md
+++ b/samples/features/security/azure-active-directory-auth/token/README.md
@@ -8,22 +8,22 @@
## About this sample
-The Token project contains a simple console application that connects to Azure SQL database using a self-signed certificate.
+The Token project contains a simple console application that connects to Azure SQL Database using a self-signed certificate.
**Software prerequisites:**
1. The `makecert.exe` utility, which is included in the Windows SDK
+ It is sometimes included in Visual Studio installations (depending on the selections made during installation). A search of your machine for `makecert.exe` would provide verification that the Windows SDK was installed.
- + If the Windows SDK was not installed, you may [download it here](http://msdn.microsoft.com/en-US/windows/desktop/aa904949)
+ + If the Windows SDK was not installed, you may [download it here](https://learn.microsoft.com/windows/apps/windows-app-sdk/downloads)
+ You can learn more about the `makecert.exe` [utility here](https://msdn.microsoft.com/library/windows/desktop/aa386968.aspx)
2. PowerShell with Azure Active Directory Module
- + To download the latest PowerShell version [see this page](https://azure.microsoft.com/en-us/documentation/articles/powershell-install-configure/#Install)
- + [Install the Azure AD PowerShell Module](https://msdn.microsoft.com/en-us/library/azure/jj151815.aspx), if it is not already installed in your client machine.
+ + To download the latest PowerShell version [see this page](https://learn.microsoft.com/powershell/azure/install-azure-powershell)
+ + [Install the Microsoft Entra ID PowerShell Module](https://learn.microsoft.com/powershell/entra-powershell/installation), if it is not already installed in your client machine.
## Run this sample
-1. Create an application account in Azure AD for your service.
+1. Create an application account in Microsoft Entra ID for your service.
- Sign in to the Azure management portal.
- Click on Azure Active Directory in the left hand navigation
- Click the directory tenant where you wish to register the sample application. This must be the same directory that is associated with your database (the server hosting your database).
@@ -31,16 +31,15 @@ The Token project contains a simple console application that connects to Azure S
- In the drawer, click Add.
- Click "Add an application my organization is developing".
- Enter mytokentest as a friendly name for the application, select "Web Application and/or Web API", and click next.
- - Assuming this application is a daemon/service and not a web application, it doesn't have a sign-in URL or app ID URI. For these two fields, enter http://mytokentest
- - While still in the Azure portal, click the Configure tab of your application.
- - Find the Client ID value and copy it into a text editor, you will need this later when configuring your application ( i.e. a4bbfe26-dbaa-4fec-8ef5-223d229f647d /see the snapshot below/)
-
+ - Assuming this application is a daemon/service and not a web application, it doesn't have a sign-in URL or app ID URI. For these two fields, enter `http://mytokentest`
+ - While still in the Azure portal, select Configure in your application.
+ - Find the Client ID value and copy it into a text editor, you will need this later when configuring your application
-2. Logon to your Azure SQL Server’s user database as an Azure AD admin and using a T-SQL command provision a contained database user for your application principal:
+2. Logon to your Azure SQL Server’s user database as an Microsoft Entra ID admin and using a T-SQL command provision a contained database user for your application principal:
```sql
CREATE USER [mytokentest] FROM EXTERNAL PROVIDER
```
- - [See this link](https://azure.microsoft.com/en-us/documentation/articles/sql-database-aad-authentication/) for more details on how to create an Azure Ad admin and a contained database user.
+ - [See this link](https://azure.microsoft.com/documentation/articles/sql-database-aad-authentication/) for more details on how to create an Microsoft Entra ID admin and a contained database user.
3. On the machine you are going to run the project on, generate and install a self-signed certificate.
- To complete this step, you will need to use `Makecert.exe`
@@ -53,10 +52,10 @@ The Token project contains a simple console application that connects to Azure S
```
c:/"Program Files (x86)/Windows Kits/8.1/bin/x64"/makecert -r -pe -n "CN=mytokentestCert" -ss My -len 2048 mytokentestCert.cer
```
-4. Add the certificate as a key for the application you created in Azure AD.
- - Click the Microsoft Azure Active Directory Module for Windows PowerShell shortcut on desktop to open a Windows PowerShell workspace that has the Azure AD cmdlets.
+4. Add the certificate as a key for the application you created in Microsoft Entra ID.
+ - Click the Microsoft Azure Active Directory Module for Windows PowerShell shortcut on desktop to open a Windows PowerShell workspace that has the Microsoft Entra ID cmdlets.
- Copy the following code snippet to a text editor.
- - `connect-msolservice` will ask for you Azure AD credentials. Please be sure to use credentials that are part of Azure AD global admin to connect and to proceed with the scripts below.
+ - `connect-msolservice` will ask for you Microsoft Entra ID credentials. Please be sure to use credentials that are part of Microsoft Entra ID global admin to connect and to proceed with the scripts below.
```
connect-msolservice
@@ -74,23 +73,21 @@ The Token project contains a simple console application that connects to Azure S
5. Configure the certificate and your application account in the *app.config* file in the project.
+ In Visual Studio, open *app.config* in the Solution Explorer
- 
- Find the app key `ida:Tenant` and replace the value with your AAD tenant name (your AAD domain)
- Find the app key `ida:ClientID` and replace the value with the Client ID for the application registration from the Azure Portal (the value from step 1).
- Find the app key `ida:Cert_Name` and replace the value with the subject name (CN) of the self-signed certificate you created
- For example:
```csharp
- //this is the AAD domain
- //this is the Client ID
+ //this is the domain
+ //this is the Client ID
//this is the Cert_name use by makecert.exe
```
+ In Visual Studio, open *Program.cs* in the Solution Explorer
- 
+ 
- Make the following changes:
```csharp
- builder["Data Source"] = "aad-managed-demo.database.windows.net"; // replace with your server name
+ builder["Data Source"] = ".database.windows.net"; // replace with your server name
builder["Initial Catalog"] = "demo"; // replace with your database name
```
-6. Run the demo. (Click *Run* or press *F5*)
- + A successful authorization should result in a message that states "Connected to the database" similar to the following:
- 
+6. Run the demo. (Select *Run* or press *F5*)
+ + A successful authorization should result in a message that includes "The access token obtained" and states "Connected to the database" and "Please press any key to stop".
diff --git a/samples/features/security/ledger/source/ContosoHR/ContosoHR.csproj b/samples/features/security/ledger/source/ContosoHR/ContosoHR.csproj
index 99e6ed6546..9be5ac6bb6 100644
--- a/samples/features/security/ledger/source/ContosoHR/ContosoHR.csproj
+++ b/samples/features/security/ledger/source/ContosoHR/ContosoHR.csproj
@@ -43,7 +43,7 @@
-
+
diff --git a/samples/features/sql-big-data-cluster/machine-learning/spark/.DS_Store b/samples/features/sql-big-data-cluster/machine-learning/spark/.DS_Store
deleted file mode 100644
index 5008ddfcf5..0000000000
Binary files a/samples/features/sql-big-data-cluster/machine-learning/spark/.DS_Store and /dev/null differ
diff --git a/samples/features/sql-big-data-cluster/security/encryption-at-rest-external-key-provider/kms_plugin_app/requirements.txt b/samples/features/sql-big-data-cluster/security/encryption-at-rest-external-key-provider/kms_plugin_app/requirements.txt
index 2b367a6821..581a49efb8 100644
--- a/samples/features/sql-big-data-cluster/security/encryption-at-rest-external-key-provider/kms_plugin_app/requirements.txt
+++ b/samples/features/sql-big-data-cluster/security/encryption-at-rest-external-key-provider/kms_plugin_app/requirements.txt
@@ -1,6 +1,6 @@
pycrypto==2.6.1
pycryptodome==3.19.1
-cryptography==41.0.4
+cryptography==43.0.1
hvac==0.10.11
-azure-identity==1.6.0
+azure-identity==1.16.1
azure-keyvault-keys==4.3.1
diff --git a/samples/features/sqlvdi/README.md b/samples/features/sqlvdi/README.md
index 11fbcf1f1e..9736b6cc5d 100644
--- a/samples/features/sqlvdi/README.md
+++ b/samples/features/sqlvdi/README.md
@@ -1,11 +1,6 @@
# SQLVDI
This folder contains the latest files and samples required to build a SQL Server VDI based backup/restore application.
-### Files available
-1. vdi.h
-2. vdierror.h
-3. vdiguid.h
-
A new **VDC_Complete** command was added to SQLVDI that indicates SQL Server has completed sending data to the VDI client. Therefore, the VDI client will be able to finish the backup before it sends response to SQL Server.
More details about this improvement in the SQLVDI protocol can be found in [KB3188454: Enhance VDI Protocol with VDC_Complete command in SQL Server] (https://support.microsoft.com/en-us/kb/3188454)
diff --git a/samples/features/sqlvdi/vdi.h b/samples/features/sqlvdi/include/vdi.h
similarity index 74%
rename from samples/features/sqlvdi/vdi.h
rename to samples/features/sqlvdi/include/vdi.h
index 97404b79e3..e7e6f56c93 100644
--- a/samples/features/sqlvdi/vdi.h
+++ b/samples/features/sqlvdi/include/vdi.h
@@ -3,17 +3,25 @@
/* this ALWAYS GENERATED file contains the definitions for the interfaces */
- /* File created by MIDL compiler version 7.00.0555 */
-/* at Tue Oct 12 14:51:44 2010
+ /* File created by MIDL compiler version 7.00.0408 */
+/* at Tue Sep 28 18:18:04 2004
*/
-
+/* Compiler settings for vdi.idl:
+ Oicf, W1, Zp8, env=Win32 (32b run)
+ protocol : dce , ms_ext, c_ext, robust
+ error checks: allocation ref bounds_check enum stub_data
+ VC __declspec() decoration level:
+ __declspec(uuid()), __declspec(selectany), __declspec(novtable)
+ DECLSPEC_UUID(), MIDL_INTERFACE()
+*/
+//@@MIDL_FILE_HEADING( )
#pragma warning( disable: 4049 ) /* more than 64k source lines */
/* verify that the version is high enough to compile this file*/
#ifndef __REQUIRED_RPCNDR_H_VERSION__
-#define __REQUIRED_RPCNDR_H_VERSION__ 440
+#define __REQUIRED_RPCNDR_H_VERSION__ 475
#endif
#include "rpc.h"
@@ -35,7 +43,7 @@
#pragma once
#endif
-/* Forward Declarations */
+/* Forward Declarations */
#ifndef __IClientVirtualDevice_FWD_DEFINED__
#define __IClientVirtualDevice_FWD_DEFINED__
@@ -78,11 +86,13 @@ typedef interface IServerVirtualDeviceSet2 IServerVirtualDeviceSet2;
#ifdef __cplusplus
extern "C"{
-#endif
+#endif
+void * __RPC_USER MIDL_user_allocate(size_t);
+void __RPC_USER MIDL_user_free( void * );
-/* interface __MIDL_itf_vdi_0000_0000 */
-/* [local] */
+/* interface __MIDL_itf_vdi_0000 */
+/* [local] */
#pragma pack(push, _vdi_h_)
@@ -115,10 +125,8 @@ enum VDFeatures
VDF_SnapshotPrepare = 0x400,
VDF_EnumFrozenFiles = 0x800,
VDF_VSSWriter = 0x1000,
- VDF_RequestComplete = 0x2000,
VDF_WriteMedia = 0x10000,
VDF_ReadMedia = 0x20000,
- VDF_CompleteEnabled = 0x40000,
VDF_LatchStats = 0x80000000,
VDF_LikePipe = 0,
VDF_LikeTape = ( ( ( ( ( VDF_FileMarks | VDF_Removable ) | VDF_Rewind ) | VDF_Position ) | VDF_SkipBlocks ) | VDF_ReversePosition ) ,
@@ -142,15 +150,13 @@ enum VDCommands
VDC_MountSnapshot = ( VDC_Snapshot + 1 ) ,
VDC_PrepareToFreeze = ( VDC_MountSnapshot + 1 ) ,
VDC_FileInfoBegin = ( VDC_PrepareToFreeze + 1 ) ,
- VDC_FileInfoEnd = ( VDC_FileInfoBegin + 1 ) ,
- VDC_GetError = (VDC_FileInfoEnd + 1),
- VDC_Complete = (VDC_GetError + 1)
+ VDC_FileInfoEnd = ( VDC_FileInfoBegin + 1 )
} ;
enum VDWhence
{ VDC_Beginning = 0,
VDC_Current = ( VDC_Beginning + 1 ) ,
- VDC_End = ( VDC_Current + 1 )
+ VDC_End = ( VDC_Current + 1 )
} ;
struct VDC_Command
{
@@ -161,66 +167,65 @@ struct VDC_Command
} ;
-extern RPC_IF_HANDLE __MIDL_itf_vdi_0000_0000_v0_0_c_ifspec;
-extern RPC_IF_HANDLE __MIDL_itf_vdi_0000_0000_v0_0_s_ifspec;
+extern RPC_IF_HANDLE __MIDL_itf_vdi_0000_v0_0_c_ifspec;
+extern RPC_IF_HANDLE __MIDL_itf_vdi_0000_v0_0_s_ifspec;
#ifndef __IClientVirtualDevice_INTERFACE_DEFINED__
#define __IClientVirtualDevice_INTERFACE_DEFINED__
/* interface IClientVirtualDevice */
-/* [object][uuid] */
+/* [object][uuid] */
EXTERN_C const IID IID_IClientVirtualDevice;
#if defined(__cplusplus) && !defined(CINTERFACE)
-
+
MIDL_INTERFACE("40700424-0080-11d2-851f-00c04fc21759")
IClientVirtualDevice : public IUnknown
{
public:
- virtual HRESULT STDMETHODCALLTYPE GetCommand(
+ virtual HRESULT STDMETHODCALLTYPE GetCommand(
/* [in] */ DWORD dwTimeOut,
/* [out] */ struct VDC_Command **ppCmd) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE CompleteCommand(
+
+ virtual HRESULT STDMETHODCALLTYPE CompleteCommand(
/* [in] */ struct VDC_Command *pCmd,
/* [in] */ DWORD dwCompletionCode,
/* [in] */ DWORD dwBytesTransferred,
/* [in] */ DWORDLONG dwlPosition) = 0;
-
+
};
-
+
#else /* C style interface */
typedef struct IClientVirtualDeviceVtbl
{
BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
IClientVirtualDevice * This,
/* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- __RPC__deref_out void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
+ /* [iid_is][out] */ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
IClientVirtualDevice * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
IClientVirtualDevice * This);
-
- HRESULT ( STDMETHODCALLTYPE *GetCommand )(
+
+ HRESULT ( STDMETHODCALLTYPE *GetCommand )(
IClientVirtualDevice * This,
/* [in] */ DWORD dwTimeOut,
/* [out] */ struct VDC_Command **ppCmd);
-
- HRESULT ( STDMETHODCALLTYPE *CompleteCommand )(
+
+ HRESULT ( STDMETHODCALLTYPE *CompleteCommand )(
IClientVirtualDevice * This,
/* [in] */ struct VDC_Command *pCmd,
/* [in] */ DWORD dwCompletionCode,
/* [in] */ DWORD dwBytesTransferred,
/* [in] */ DWORDLONG dwlPosition);
-
+
END_INTERFACE
} IClientVirtualDeviceVtbl;
@@ -229,26 +234,26 @@ EXTERN_C const IID IID_IClientVirtualDevice;
CONST_VTBL struct IClientVirtualDeviceVtbl *lpVtbl;
};
-
+
#ifdef COBJMACROS
#define IClientVirtualDevice_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
#define IClientVirtualDevice_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
+ ( (This)->lpVtbl -> AddRef(This) )
#define IClientVirtualDevice_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
+ ( (This)->lpVtbl -> Release(This) )
#define IClientVirtualDevice_GetCommand(This,dwTimeOut,ppCmd) \
- ( (This)->lpVtbl -> GetCommand(This,dwTimeOut,ppCmd) )
+ ( (This)->lpVtbl -> GetCommand(This,dwTimeOut,ppCmd) )
#define IClientVirtualDevice_CompleteCommand(This,pCmd,dwCompletionCode,dwBytesTransferred,dwlPosition) \
- ( (This)->lpVtbl -> CompleteCommand(This,pCmd,dwCompletionCode,dwBytesTransferred,dwlPosition) )
+ ( (This)->lpVtbl -> CompleteCommand(This,pCmd,dwCompletionCode,dwBytesTransferred,dwlPosition) )
#endif /* COBJMACROS */
@@ -257,7 +262,7 @@ EXTERN_C const IID IID_IClientVirtualDevice;
-HRESULT STDMETHODCALLTYPE IClientVirtualDevice_GetCommand_Proxy(
+HRESULT STDMETHODCALLTYPE IClientVirtualDevice_GetCommand_Proxy(
IClientVirtualDevice * This,
/* [in] */ DWORD dwTimeOut,
/* [out] */ struct VDC_Command **ppCmd);
@@ -270,7 +275,7 @@ void __RPC_STUB IClientVirtualDevice_GetCommand_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IClientVirtualDevice_CompleteCommand_Proxy(
+HRESULT STDMETHODCALLTYPE IClientVirtualDevice_CompleteCommand_Proxy(
IClientVirtualDevice * This,
/* [in] */ struct VDC_Command *pCmd,
/* [in] */ DWORD dwCompletionCode,
@@ -293,99 +298,98 @@ void __RPC_STUB IClientVirtualDevice_CompleteCommand_Stub(
#define __IClientVirtualDeviceSet_INTERFACE_DEFINED__
/* interface IClientVirtualDeviceSet */
-/* [object][uuid] */
+/* [object][uuid] */
EXTERN_C const IID IID_IClientVirtualDeviceSet;
#if defined(__cplusplus) && !defined(CINTERFACE)
-
+
MIDL_INTERFACE("40700425-0080-11d2-851f-00c04fc21759")
IClientVirtualDeviceSet : public IUnknown
{
public:
- virtual HRESULT STDMETHODCALLTYPE Create(
+ virtual HRESULT STDMETHODCALLTYPE Create(
/* [in] */ LPCWSTR lpName,
/* [in] */ struct VDConfig *pCfg) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE GetConfiguration(
+
+ virtual HRESULT STDMETHODCALLTYPE GetConfiguration(
/* [in] */ DWORD dwTimeOut,
/* [out] */ struct VDConfig *pCfg) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE OpenDevice(
+
+ virtual HRESULT STDMETHODCALLTYPE OpenDevice(
/* [in] */ LPCWSTR lpName,
/* [out] */ IClientVirtualDevice **ppVirtualDevice) = 0;
-
+
virtual HRESULT STDMETHODCALLTYPE Close( void) = 0;
-
+
virtual HRESULT STDMETHODCALLTYPE SignalAbort( void) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE OpenInSecondary(
+
+ virtual HRESULT STDMETHODCALLTYPE OpenInSecondary(
/* [in] */ LPCWSTR lpSetName) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE GetBufferHandle(
+
+ virtual HRESULT STDMETHODCALLTYPE GetBufferHandle(
/* [in] */ BYTE *pBuffer,
/* [out] */ DWORD *pBufferHandle) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE MapBufferHandle(
+
+ virtual HRESULT STDMETHODCALLTYPE MapBufferHandle(
/* [in] */ DWORD dwBuffer,
/* [out] */ BYTE **ppBuffer) = 0;
-
+
};
-
+
#else /* C style interface */
typedef struct IClientVirtualDeviceSetVtbl
{
BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
IClientVirtualDeviceSet * This,
/* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- __RPC__deref_out void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
+ /* [iid_is][out] */ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
IClientVirtualDeviceSet * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
IClientVirtualDeviceSet * This);
-
- HRESULT ( STDMETHODCALLTYPE *Create )(
+
+ HRESULT ( STDMETHODCALLTYPE *Create )(
IClientVirtualDeviceSet * This,
/* [in] */ LPCWSTR lpName,
/* [in] */ struct VDConfig *pCfg);
-
- HRESULT ( STDMETHODCALLTYPE *GetConfiguration )(
+
+ HRESULT ( STDMETHODCALLTYPE *GetConfiguration )(
IClientVirtualDeviceSet * This,
/* [in] */ DWORD dwTimeOut,
/* [out] */ struct VDConfig *pCfg);
-
- HRESULT ( STDMETHODCALLTYPE *OpenDevice )(
+
+ HRESULT ( STDMETHODCALLTYPE *OpenDevice )(
IClientVirtualDeviceSet * This,
/* [in] */ LPCWSTR lpName,
/* [out] */ IClientVirtualDevice **ppVirtualDevice);
-
- HRESULT ( STDMETHODCALLTYPE *Close )(
+
+ HRESULT ( STDMETHODCALLTYPE *Close )(
IClientVirtualDeviceSet * This);
-
- HRESULT ( STDMETHODCALLTYPE *SignalAbort )(
+
+ HRESULT ( STDMETHODCALLTYPE *SignalAbort )(
IClientVirtualDeviceSet * This);
-
- HRESULT ( STDMETHODCALLTYPE *OpenInSecondary )(
+
+ HRESULT ( STDMETHODCALLTYPE *OpenInSecondary )(
IClientVirtualDeviceSet * This,
/* [in] */ LPCWSTR lpSetName);
-
- HRESULT ( STDMETHODCALLTYPE *GetBufferHandle )(
+
+ HRESULT ( STDMETHODCALLTYPE *GetBufferHandle )(
IClientVirtualDeviceSet * This,
/* [in] */ BYTE *pBuffer,
/* [out] */ DWORD *pBufferHandle);
-
- HRESULT ( STDMETHODCALLTYPE *MapBufferHandle )(
+
+ HRESULT ( STDMETHODCALLTYPE *MapBufferHandle )(
IClientVirtualDeviceSet * This,
/* [in] */ DWORD dwBuffer,
/* [out] */ BYTE **ppBuffer);
-
+
END_INTERFACE
} IClientVirtualDeviceSetVtbl;
@@ -394,44 +398,44 @@ EXTERN_C const IID IID_IClientVirtualDeviceSet;
CONST_VTBL struct IClientVirtualDeviceSetVtbl *lpVtbl;
};
-
+
#ifdef COBJMACROS
#define IClientVirtualDeviceSet_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
#define IClientVirtualDeviceSet_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
+ ( (This)->lpVtbl -> AddRef(This) )
#define IClientVirtualDeviceSet_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
+ ( (This)->lpVtbl -> Release(This) )
#define IClientVirtualDeviceSet_Create(This,lpName,pCfg) \
- ( (This)->lpVtbl -> Create(This,lpName,pCfg) )
+ ( (This)->lpVtbl -> Create(This,lpName,pCfg) )
#define IClientVirtualDeviceSet_GetConfiguration(This,dwTimeOut,pCfg) \
- ( (This)->lpVtbl -> GetConfiguration(This,dwTimeOut,pCfg) )
+ ( (This)->lpVtbl -> GetConfiguration(This,dwTimeOut,pCfg) )
#define IClientVirtualDeviceSet_OpenDevice(This,lpName,ppVirtualDevice) \
- ( (This)->lpVtbl -> OpenDevice(This,lpName,ppVirtualDevice) )
+ ( (This)->lpVtbl -> OpenDevice(This,lpName,ppVirtualDevice) )
#define IClientVirtualDeviceSet_Close(This) \
- ( (This)->lpVtbl -> Close(This) )
+ ( (This)->lpVtbl -> Close(This) )
#define IClientVirtualDeviceSet_SignalAbort(This) \
- ( (This)->lpVtbl -> SignalAbort(This) )
+ ( (This)->lpVtbl -> SignalAbort(This) )
#define IClientVirtualDeviceSet_OpenInSecondary(This,lpSetName) \
- ( (This)->lpVtbl -> OpenInSecondary(This,lpSetName) )
+ ( (This)->lpVtbl -> OpenInSecondary(This,lpSetName) )
#define IClientVirtualDeviceSet_GetBufferHandle(This,pBuffer,pBufferHandle) \
- ( (This)->lpVtbl -> GetBufferHandle(This,pBuffer,pBufferHandle) )
+ ( (This)->lpVtbl -> GetBufferHandle(This,pBuffer,pBufferHandle) )
#define IClientVirtualDeviceSet_MapBufferHandle(This,dwBuffer,ppBuffer) \
- ( (This)->lpVtbl -> MapBufferHandle(This,dwBuffer,ppBuffer) )
+ ( (This)->lpVtbl -> MapBufferHandle(This,dwBuffer,ppBuffer) )
#endif /* COBJMACROS */
@@ -440,7 +444,7 @@ EXTERN_C const IID IID_IClientVirtualDeviceSet;
-HRESULT STDMETHODCALLTYPE IClientVirtualDeviceSet_Create_Proxy(
+HRESULT STDMETHODCALLTYPE IClientVirtualDeviceSet_Create_Proxy(
IClientVirtualDeviceSet * This,
/* [in] */ LPCWSTR lpName,
/* [in] */ struct VDConfig *pCfg);
@@ -453,7 +457,7 @@ void __RPC_STUB IClientVirtualDeviceSet_Create_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IClientVirtualDeviceSet_GetConfiguration_Proxy(
+HRESULT STDMETHODCALLTYPE IClientVirtualDeviceSet_GetConfiguration_Proxy(
IClientVirtualDeviceSet * This,
/* [in] */ DWORD dwTimeOut,
/* [out] */ struct VDConfig *pCfg);
@@ -466,7 +470,7 @@ void __RPC_STUB IClientVirtualDeviceSet_GetConfiguration_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IClientVirtualDeviceSet_OpenDevice_Proxy(
+HRESULT STDMETHODCALLTYPE IClientVirtualDeviceSet_OpenDevice_Proxy(
IClientVirtualDeviceSet * This,
/* [in] */ LPCWSTR lpName,
/* [out] */ IClientVirtualDevice **ppVirtualDevice);
@@ -479,7 +483,7 @@ void __RPC_STUB IClientVirtualDeviceSet_OpenDevice_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IClientVirtualDeviceSet_Close_Proxy(
+HRESULT STDMETHODCALLTYPE IClientVirtualDeviceSet_Close_Proxy(
IClientVirtualDeviceSet * This);
@@ -490,7 +494,7 @@ void __RPC_STUB IClientVirtualDeviceSet_Close_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IClientVirtualDeviceSet_SignalAbort_Proxy(
+HRESULT STDMETHODCALLTYPE IClientVirtualDeviceSet_SignalAbort_Proxy(
IClientVirtualDeviceSet * This);
@@ -501,7 +505,7 @@ void __RPC_STUB IClientVirtualDeviceSet_SignalAbort_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IClientVirtualDeviceSet_OpenInSecondary_Proxy(
+HRESULT STDMETHODCALLTYPE IClientVirtualDeviceSet_OpenInSecondary_Proxy(
IClientVirtualDeviceSet * This,
/* [in] */ LPCWSTR lpSetName);
@@ -513,7 +517,7 @@ void __RPC_STUB IClientVirtualDeviceSet_OpenInSecondary_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IClientVirtualDeviceSet_GetBufferHandle_Proxy(
+HRESULT STDMETHODCALLTYPE IClientVirtualDeviceSet_GetBufferHandle_Proxy(
IClientVirtualDeviceSet * This,
/* [in] */ BYTE *pBuffer,
/* [out] */ DWORD *pBufferHandle);
@@ -526,7 +530,7 @@ void __RPC_STUB IClientVirtualDeviceSet_GetBufferHandle_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IClientVirtualDeviceSet_MapBufferHandle_Proxy(
+HRESULT STDMETHODCALLTYPE IClientVirtualDeviceSet_MapBufferHandle_Proxy(
IClientVirtualDeviceSet * This,
/* [in] */ DWORD dwBuffer,
/* [out] */ BYTE **ppBuffer);
@@ -547,92 +551,91 @@ void __RPC_STUB IClientVirtualDeviceSet_MapBufferHandle_Stub(
#define __IClientVirtualDeviceSet2_INTERFACE_DEFINED__
/* interface IClientVirtualDeviceSet2 */
-/* [object][uuid] */
+/* [object][uuid] */
EXTERN_C const IID IID_IClientVirtualDeviceSet2;
#if defined(__cplusplus) && !defined(CINTERFACE)
-
+
MIDL_INTERFACE("d0e6eb07-7a62-11d2-8573-00c04fc21759")
IClientVirtualDeviceSet2 : public IClientVirtualDeviceSet
{
public:
- virtual HRESULT STDMETHODCALLTYPE CreateEx(
+ virtual HRESULT STDMETHODCALLTYPE CreateEx(
/* [in] */ LPCWSTR lpInstanceName,
/* [in] */ LPCWSTR lpName,
/* [in] */ struct VDConfig *pCfg) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE OpenInSecondaryEx(
+
+ virtual HRESULT STDMETHODCALLTYPE OpenInSecondaryEx(
/* [in] */ LPCWSTR lpInstanceName,
/* [in] */ LPCWSTR lpSetName) = 0;
-
+
};
-
+
#else /* C style interface */
typedef struct IClientVirtualDeviceSet2Vtbl
{
BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
IClientVirtualDeviceSet2 * This,
/* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- __RPC__deref_out void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
+ /* [iid_is][out] */ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
IClientVirtualDeviceSet2 * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
IClientVirtualDeviceSet2 * This);
-
- HRESULT ( STDMETHODCALLTYPE *Create )(
+
+ HRESULT ( STDMETHODCALLTYPE *Create )(
IClientVirtualDeviceSet2 * This,
/* [in] */ LPCWSTR lpName,
/* [in] */ struct VDConfig *pCfg);
-
- HRESULT ( STDMETHODCALLTYPE *GetConfiguration )(
+
+ HRESULT ( STDMETHODCALLTYPE *GetConfiguration )(
IClientVirtualDeviceSet2 * This,
/* [in] */ DWORD dwTimeOut,
/* [out] */ struct VDConfig *pCfg);
-
- HRESULT ( STDMETHODCALLTYPE *OpenDevice )(
+
+ HRESULT ( STDMETHODCALLTYPE *OpenDevice )(
IClientVirtualDeviceSet2 * This,
/* [in] */ LPCWSTR lpName,
/* [out] */ IClientVirtualDevice **ppVirtualDevice);
-
- HRESULT ( STDMETHODCALLTYPE *Close )(
+
+ HRESULT ( STDMETHODCALLTYPE *Close )(
IClientVirtualDeviceSet2 * This);
-
- HRESULT ( STDMETHODCALLTYPE *SignalAbort )(
+
+ HRESULT ( STDMETHODCALLTYPE *SignalAbort )(
IClientVirtualDeviceSet2 * This);
-
- HRESULT ( STDMETHODCALLTYPE *OpenInSecondary )(
+
+ HRESULT ( STDMETHODCALLTYPE *OpenInSecondary )(
IClientVirtualDeviceSet2 * This,
/* [in] */ LPCWSTR lpSetName);
-
- HRESULT ( STDMETHODCALLTYPE *GetBufferHandle )(
+
+ HRESULT ( STDMETHODCALLTYPE *GetBufferHandle )(
IClientVirtualDeviceSet2 * This,
/* [in] */ BYTE *pBuffer,
/* [out] */ DWORD *pBufferHandle);
-
- HRESULT ( STDMETHODCALLTYPE *MapBufferHandle )(
+
+ HRESULT ( STDMETHODCALLTYPE *MapBufferHandle )(
IClientVirtualDeviceSet2 * This,
/* [in] */ DWORD dwBuffer,
/* [out] */ BYTE **ppBuffer);
-
- HRESULT ( STDMETHODCALLTYPE *CreateEx )(
+
+ HRESULT ( STDMETHODCALLTYPE *CreateEx )(
IClientVirtualDeviceSet2 * This,
/* [in] */ LPCWSTR lpInstanceName,
/* [in] */ LPCWSTR lpName,
/* [in] */ struct VDConfig *pCfg);
-
- HRESULT ( STDMETHODCALLTYPE *OpenInSecondaryEx )(
+
+ HRESULT ( STDMETHODCALLTYPE *OpenInSecondaryEx )(
IClientVirtualDeviceSet2 * This,
/* [in] */ LPCWSTR lpInstanceName,
/* [in] */ LPCWSTR lpSetName);
-
+
END_INTERFACE
} IClientVirtualDeviceSet2Vtbl;
@@ -641,51 +644,51 @@ EXTERN_C const IID IID_IClientVirtualDeviceSet2;
CONST_VTBL struct IClientVirtualDeviceSet2Vtbl *lpVtbl;
};
-
+
#ifdef COBJMACROS
#define IClientVirtualDeviceSet2_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
#define IClientVirtualDeviceSet2_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
+ ( (This)->lpVtbl -> AddRef(This) )
#define IClientVirtualDeviceSet2_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
+ ( (This)->lpVtbl -> Release(This) )
#define IClientVirtualDeviceSet2_Create(This,lpName,pCfg) \
- ( (This)->lpVtbl -> Create(This,lpName,pCfg) )
+ ( (This)->lpVtbl -> Create(This,lpName,pCfg) )
#define IClientVirtualDeviceSet2_GetConfiguration(This,dwTimeOut,pCfg) \
- ( (This)->lpVtbl -> GetConfiguration(This,dwTimeOut,pCfg) )
+ ( (This)->lpVtbl -> GetConfiguration(This,dwTimeOut,pCfg) )
#define IClientVirtualDeviceSet2_OpenDevice(This,lpName,ppVirtualDevice) \
- ( (This)->lpVtbl -> OpenDevice(This,lpName,ppVirtualDevice) )
+ ( (This)->lpVtbl -> OpenDevice(This,lpName,ppVirtualDevice) )
#define IClientVirtualDeviceSet2_Close(This) \
- ( (This)->lpVtbl -> Close(This) )
+ ( (This)->lpVtbl -> Close(This) )
#define IClientVirtualDeviceSet2_SignalAbort(This) \
- ( (This)->lpVtbl -> SignalAbort(This) )
+ ( (This)->lpVtbl -> SignalAbort(This) )
#define IClientVirtualDeviceSet2_OpenInSecondary(This,lpSetName) \
- ( (This)->lpVtbl -> OpenInSecondary(This,lpSetName) )
+ ( (This)->lpVtbl -> OpenInSecondary(This,lpSetName) )
#define IClientVirtualDeviceSet2_GetBufferHandle(This,pBuffer,pBufferHandle) \
- ( (This)->lpVtbl -> GetBufferHandle(This,pBuffer,pBufferHandle) )
+ ( (This)->lpVtbl -> GetBufferHandle(This,pBuffer,pBufferHandle) )
#define IClientVirtualDeviceSet2_MapBufferHandle(This,dwBuffer,ppBuffer) \
- ( (This)->lpVtbl -> MapBufferHandle(This,dwBuffer,ppBuffer) )
+ ( (This)->lpVtbl -> MapBufferHandle(This,dwBuffer,ppBuffer) )
#define IClientVirtualDeviceSet2_CreateEx(This,lpInstanceName,lpName,pCfg) \
- ( (This)->lpVtbl -> CreateEx(This,lpInstanceName,lpName,pCfg) )
+ ( (This)->lpVtbl -> CreateEx(This,lpInstanceName,lpName,pCfg) )
#define IClientVirtualDeviceSet2_OpenInSecondaryEx(This,lpInstanceName,lpSetName) \
- ( (This)->lpVtbl -> OpenInSecondaryEx(This,lpInstanceName,lpSetName) )
+ ( (This)->lpVtbl -> OpenInSecondaryEx(This,lpInstanceName,lpSetName) )
#endif /* COBJMACROS */
@@ -694,7 +697,7 @@ EXTERN_C const IID IID_IClientVirtualDeviceSet2;
-HRESULT STDMETHODCALLTYPE IClientVirtualDeviceSet2_CreateEx_Proxy(
+HRESULT STDMETHODCALLTYPE IClientVirtualDeviceSet2_CreateEx_Proxy(
IClientVirtualDeviceSet2 * This,
/* [in] */ LPCWSTR lpInstanceName,
/* [in] */ LPCWSTR lpName,
@@ -708,7 +711,7 @@ void __RPC_STUB IClientVirtualDeviceSet2_CreateEx_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IClientVirtualDeviceSet2_OpenInSecondaryEx_Proxy(
+HRESULT STDMETHODCALLTYPE IClientVirtualDeviceSet2_OpenInSecondaryEx_Proxy(
IClientVirtualDeviceSet2 * This,
/* [in] */ LPCWSTR lpInstanceName,
/* [in] */ LPCWSTR lpSetName);
@@ -725,8 +728,8 @@ void __RPC_STUB IClientVirtualDeviceSet2_OpenInSecondaryEx_Stub(
#endif /* __IClientVirtualDeviceSet2_INTERFACE_DEFINED__ */
-/* interface __MIDL_itf_vdi_0000_0003 */
-/* [local] */
+/* interface __MIDL_itf_vdi_0011 */
+/* [local] */
struct VDS_Command
{
@@ -742,56 +745,55 @@ struct VDS_Command
} ;
-extern RPC_IF_HANDLE __MIDL_itf_vdi_0000_0003_v0_0_c_ifspec;
-extern RPC_IF_HANDLE __MIDL_itf_vdi_0000_0003_v0_0_s_ifspec;
+extern RPC_IF_HANDLE __MIDL_itf_vdi_0011_v0_0_c_ifspec;
+extern RPC_IF_HANDLE __MIDL_itf_vdi_0011_v0_0_s_ifspec;
#ifndef __IServerVirtualDevice_INTERFACE_DEFINED__
#define __IServerVirtualDevice_INTERFACE_DEFINED__
/* interface IServerVirtualDevice */
-/* [object][uuid] */
+/* [object][uuid] */
EXTERN_C const IID IID_IServerVirtualDevice;
#if defined(__cplusplus) && !defined(CINTERFACE)
-
+
MIDL_INTERFACE("b5e7a131-a7bd-11d1-84c2-00c04fc21759")
IServerVirtualDevice : public IUnknown
{
public:
- virtual HRESULT STDMETHODCALLTYPE SendCommand(
+ virtual HRESULT STDMETHODCALLTYPE SendCommand(
/* [in] */ struct VDS_Command *pCmd) = 0;
-
+
virtual HRESULT STDMETHODCALLTYPE CloseDevice( void) = 0;
-
+
};
-
+
#else /* C style interface */
typedef struct IServerVirtualDeviceVtbl
{
BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
IServerVirtualDevice * This,
/* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- __RPC__deref_out void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
+ /* [iid_is][out] */ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
IServerVirtualDevice * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
IServerVirtualDevice * This);
-
- HRESULT ( STDMETHODCALLTYPE *SendCommand )(
+
+ HRESULT ( STDMETHODCALLTYPE *SendCommand )(
IServerVirtualDevice * This,
/* [in] */ struct VDS_Command *pCmd);
-
- HRESULT ( STDMETHODCALLTYPE *CloseDevice )(
+
+ HRESULT ( STDMETHODCALLTYPE *CloseDevice )(
IServerVirtualDevice * This);
-
+
END_INTERFACE
} IServerVirtualDeviceVtbl;
@@ -800,26 +802,26 @@ EXTERN_C const IID IID_IServerVirtualDevice;
CONST_VTBL struct IServerVirtualDeviceVtbl *lpVtbl;
};
-
+
#ifdef COBJMACROS
#define IServerVirtualDevice_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
#define IServerVirtualDevice_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
+ ( (This)->lpVtbl -> AddRef(This) )
#define IServerVirtualDevice_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
+ ( (This)->lpVtbl -> Release(This) )
#define IServerVirtualDevice_SendCommand(This,pCmd) \
- ( (This)->lpVtbl -> SendCommand(This,pCmd) )
+ ( (This)->lpVtbl -> SendCommand(This,pCmd) )
#define IServerVirtualDevice_CloseDevice(This) \
- ( (This)->lpVtbl -> CloseDevice(This) )
+ ( (This)->lpVtbl -> CloseDevice(This) )
#endif /* COBJMACROS */
@@ -828,7 +830,7 @@ EXTERN_C const IID IID_IServerVirtualDevice;
-HRESULT STDMETHODCALLTYPE IServerVirtualDevice_SendCommand_Proxy(
+HRESULT STDMETHODCALLTYPE IServerVirtualDevice_SendCommand_Proxy(
IServerVirtualDevice * This,
/* [in] */ struct VDS_Command *pCmd);
@@ -840,7 +842,7 @@ void __RPC_STUB IServerVirtualDevice_SendCommand_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IServerVirtualDevice_CloseDevice_Proxy(
+HRESULT STDMETHODCALLTYPE IServerVirtualDevice_CloseDevice_Proxy(
IServerVirtualDevice * This);
@@ -859,109 +861,108 @@ void __RPC_STUB IServerVirtualDevice_CloseDevice_Stub(
#define __IServerVirtualDeviceSet_INTERFACE_DEFINED__
/* interface IServerVirtualDeviceSet */
-/* [object][uuid] */
+/* [object][uuid] */
EXTERN_C const IID IID_IServerVirtualDeviceSet;
#if defined(__cplusplus) && !defined(CINTERFACE)
-
+
MIDL_INTERFACE("b5e7a132-a7bd-11d1-84c2-00c04fc21759")
IServerVirtualDeviceSet : public IUnknown
{
public:
- virtual HRESULT STDMETHODCALLTYPE Open(
+ virtual HRESULT STDMETHODCALLTYPE Open(
/* [in] */ LPCWSTR lpName) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE GetConfiguration(
+
+ virtual HRESULT STDMETHODCALLTYPE GetConfiguration(
/* [out] */ struct VDConfig *pCfg) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE SetConfiguration(
+
+ virtual HRESULT STDMETHODCALLTYPE SetConfiguration(
/* [in] */ struct VDConfig *pCfg) = 0;
-
+
virtual HRESULT STDMETHODCALLTYPE ExecuteCompletionAgent( void) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE OpenDevice(
+
+ virtual HRESULT STDMETHODCALLTYPE OpenDevice(
/* [in] */ LPCWSTR lpName,
/* [out] */ IServerVirtualDevice **ppVirtualDevice) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE AllocateBuffer(
+
+ virtual HRESULT STDMETHODCALLTYPE AllocateBuffer(
/* [out] */ BYTE **ppBuffer,
/* [in] */ DWORD dwSize,
/* [in] */ DWORD dwAlignment) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE FreeBuffer(
+
+ virtual HRESULT STDMETHODCALLTYPE FreeBuffer(
/* [in] */ BYTE *pBuffer,
/* [in] */ DWORD dwSize) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE IsSharedBuffer(
+
+ virtual HRESULT STDMETHODCALLTYPE IsSharedBuffer(
/* [in] */ BYTE *pBuffer) = 0;
-
+
virtual HRESULT STDMETHODCALLTYPE SignalAbort( void) = 0;
-
+
virtual HRESULT STDMETHODCALLTYPE Close( void) = 0;
-
+
};
-
+
#else /* C style interface */
typedef struct IServerVirtualDeviceSetVtbl
{
BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
IServerVirtualDeviceSet * This,
/* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- __RPC__deref_out void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
+ /* [iid_is][out] */ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
IServerVirtualDeviceSet * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
IServerVirtualDeviceSet * This);
-
- HRESULT ( STDMETHODCALLTYPE *Open )(
+
+ HRESULT ( STDMETHODCALLTYPE *Open )(
IServerVirtualDeviceSet * This,
/* [in] */ LPCWSTR lpName);
-
- HRESULT ( STDMETHODCALLTYPE *GetConfiguration )(
+
+ HRESULT ( STDMETHODCALLTYPE *GetConfiguration )(
IServerVirtualDeviceSet * This,
/* [out] */ struct VDConfig *pCfg);
-
- HRESULT ( STDMETHODCALLTYPE *SetConfiguration )(
+
+ HRESULT ( STDMETHODCALLTYPE *SetConfiguration )(
IServerVirtualDeviceSet * This,
/* [in] */ struct VDConfig *pCfg);
-
- HRESULT ( STDMETHODCALLTYPE *ExecuteCompletionAgent )(
+
+ HRESULT ( STDMETHODCALLTYPE *ExecuteCompletionAgent )(
IServerVirtualDeviceSet * This);
-
- HRESULT ( STDMETHODCALLTYPE *OpenDevice )(
+
+ HRESULT ( STDMETHODCALLTYPE *OpenDevice )(
IServerVirtualDeviceSet * This,
/* [in] */ LPCWSTR lpName,
/* [out] */ IServerVirtualDevice **ppVirtualDevice);
-
- HRESULT ( STDMETHODCALLTYPE *AllocateBuffer )(
+
+ HRESULT ( STDMETHODCALLTYPE *AllocateBuffer )(
IServerVirtualDeviceSet * This,
/* [out] */ BYTE **ppBuffer,
/* [in] */ DWORD dwSize,
/* [in] */ DWORD dwAlignment);
-
- HRESULT ( STDMETHODCALLTYPE *FreeBuffer )(
+
+ HRESULT ( STDMETHODCALLTYPE *FreeBuffer )(
IServerVirtualDeviceSet * This,
/* [in] */ BYTE *pBuffer,
/* [in] */ DWORD dwSize);
-
- HRESULT ( STDMETHODCALLTYPE *IsSharedBuffer )(
+
+ HRESULT ( STDMETHODCALLTYPE *IsSharedBuffer )(
IServerVirtualDeviceSet * This,
/* [in] */ BYTE *pBuffer);
-
- HRESULT ( STDMETHODCALLTYPE *SignalAbort )(
+
+ HRESULT ( STDMETHODCALLTYPE *SignalAbort )(
IServerVirtualDeviceSet * This);
-
- HRESULT ( STDMETHODCALLTYPE *Close )(
+
+ HRESULT ( STDMETHODCALLTYPE *Close )(
IServerVirtualDeviceSet * This);
-
+
END_INTERFACE
} IServerVirtualDeviceSetVtbl;
@@ -970,50 +971,50 @@ EXTERN_C const IID IID_IServerVirtualDeviceSet;
CONST_VTBL struct IServerVirtualDeviceSetVtbl *lpVtbl;
};
-
+
#ifdef COBJMACROS
#define IServerVirtualDeviceSet_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
#define IServerVirtualDeviceSet_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
+ ( (This)->lpVtbl -> AddRef(This) )
#define IServerVirtualDeviceSet_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
+ ( (This)->lpVtbl -> Release(This) )
#define IServerVirtualDeviceSet_Open(This,lpName) \
- ( (This)->lpVtbl -> Open(This,lpName) )
+ ( (This)->lpVtbl -> Open(This,lpName) )
#define IServerVirtualDeviceSet_GetConfiguration(This,pCfg) \
- ( (This)->lpVtbl -> GetConfiguration(This,pCfg) )
+ ( (This)->lpVtbl -> GetConfiguration(This,pCfg) )
#define IServerVirtualDeviceSet_SetConfiguration(This,pCfg) \
- ( (This)->lpVtbl -> SetConfiguration(This,pCfg) )
+ ( (This)->lpVtbl -> SetConfiguration(This,pCfg) )
#define IServerVirtualDeviceSet_ExecuteCompletionAgent(This) \
- ( (This)->lpVtbl -> ExecuteCompletionAgent(This) )
+ ( (This)->lpVtbl -> ExecuteCompletionAgent(This) )
#define IServerVirtualDeviceSet_OpenDevice(This,lpName,ppVirtualDevice) \
- ( (This)->lpVtbl -> OpenDevice(This,lpName,ppVirtualDevice) )
+ ( (This)->lpVtbl -> OpenDevice(This,lpName,ppVirtualDevice) )
#define IServerVirtualDeviceSet_AllocateBuffer(This,ppBuffer,dwSize,dwAlignment) \
- ( (This)->lpVtbl -> AllocateBuffer(This,ppBuffer,dwSize,dwAlignment) )
+ ( (This)->lpVtbl -> AllocateBuffer(This,ppBuffer,dwSize,dwAlignment) )
#define IServerVirtualDeviceSet_FreeBuffer(This,pBuffer,dwSize) \
- ( (This)->lpVtbl -> FreeBuffer(This,pBuffer,dwSize) )
+ ( (This)->lpVtbl -> FreeBuffer(This,pBuffer,dwSize) )
#define IServerVirtualDeviceSet_IsSharedBuffer(This,pBuffer) \
- ( (This)->lpVtbl -> IsSharedBuffer(This,pBuffer) )
+ ( (This)->lpVtbl -> IsSharedBuffer(This,pBuffer) )
#define IServerVirtualDeviceSet_SignalAbort(This) \
- ( (This)->lpVtbl -> SignalAbort(This) )
+ ( (This)->lpVtbl -> SignalAbort(This) )
#define IServerVirtualDeviceSet_Close(This) \
- ( (This)->lpVtbl -> Close(This) )
+ ( (This)->lpVtbl -> Close(This) )
#endif /* COBJMACROS */
@@ -1022,7 +1023,7 @@ EXTERN_C const IID IID_IServerVirtualDeviceSet;
-HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet_Open_Proxy(
+HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet_Open_Proxy(
IServerVirtualDeviceSet * This,
/* [in] */ LPCWSTR lpName);
@@ -1034,7 +1035,7 @@ void __RPC_STUB IServerVirtualDeviceSet_Open_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet_GetConfiguration_Proxy(
+HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet_GetConfiguration_Proxy(
IServerVirtualDeviceSet * This,
/* [out] */ struct VDConfig *pCfg);
@@ -1046,7 +1047,7 @@ void __RPC_STUB IServerVirtualDeviceSet_GetConfiguration_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet_SetConfiguration_Proxy(
+HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet_SetConfiguration_Proxy(
IServerVirtualDeviceSet * This,
/* [in] */ struct VDConfig *pCfg);
@@ -1058,7 +1059,7 @@ void __RPC_STUB IServerVirtualDeviceSet_SetConfiguration_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet_ExecuteCompletionAgent_Proxy(
+HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet_ExecuteCompletionAgent_Proxy(
IServerVirtualDeviceSet * This);
@@ -1069,7 +1070,7 @@ void __RPC_STUB IServerVirtualDeviceSet_ExecuteCompletionAgent_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet_OpenDevice_Proxy(
+HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet_OpenDevice_Proxy(
IServerVirtualDeviceSet * This,
/* [in] */ LPCWSTR lpName,
/* [out] */ IServerVirtualDevice **ppVirtualDevice);
@@ -1082,7 +1083,7 @@ void __RPC_STUB IServerVirtualDeviceSet_OpenDevice_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet_AllocateBuffer_Proxy(
+HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet_AllocateBuffer_Proxy(
IServerVirtualDeviceSet * This,
/* [out] */ BYTE **ppBuffer,
/* [in] */ DWORD dwSize,
@@ -1096,7 +1097,7 @@ void __RPC_STUB IServerVirtualDeviceSet_AllocateBuffer_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet_FreeBuffer_Proxy(
+HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet_FreeBuffer_Proxy(
IServerVirtualDeviceSet * This,
/* [in] */ BYTE *pBuffer,
/* [in] */ DWORD dwSize);
@@ -1109,7 +1110,7 @@ void __RPC_STUB IServerVirtualDeviceSet_FreeBuffer_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet_IsSharedBuffer_Proxy(
+HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet_IsSharedBuffer_Proxy(
IServerVirtualDeviceSet * This,
/* [in] */ BYTE *pBuffer);
@@ -1121,7 +1122,7 @@ void __RPC_STUB IServerVirtualDeviceSet_IsSharedBuffer_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet_SignalAbort_Proxy(
+HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet_SignalAbort_Proxy(
IServerVirtualDeviceSet * This);
@@ -1132,7 +1133,7 @@ void __RPC_STUB IServerVirtualDeviceSet_SignalAbort_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet_Close_Proxy(
+HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet_Close_Proxy(
IServerVirtualDeviceSet * This);
@@ -1151,146 +1152,145 @@ void __RPC_STUB IServerVirtualDeviceSet_Close_Stub(
#define __IServerVirtualDeviceSet2_INTERFACE_DEFINED__
/* interface IServerVirtualDeviceSet2 */
-/* [object][uuid] */
+/* [object][uuid] */
EXTERN_C const IID IID_IServerVirtualDeviceSet2;
#if defined(__cplusplus) && !defined(CINTERFACE)
-
+
MIDL_INTERFACE("AECBD0D6-24C6-11d3-85B7-00C04FC21759")
IServerVirtualDeviceSet2 : public IUnknown
{
public:
- virtual HRESULT STDMETHODCALLTYPE Open(
+ virtual HRESULT STDMETHODCALLTYPE Open(
/* [in] */ LPCWSTR lpInstanceName,
/* [in] */ LPCWSTR lpSetName) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE GetConfiguration(
+
+ virtual HRESULT STDMETHODCALLTYPE GetConfiguration(
/* [out] */ struct VDConfig *pCfg) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE BeginConfiguration(
+
+ virtual HRESULT STDMETHODCALLTYPE BeginConfiguration(
/* [in] */ DWORD dwFeatures,
/* [in] */ DWORD dwBlockSize,
/* [in] */ DWORD dwAlignment,
/* [in] */ DWORD dwMaxTransferSize,
/* [in] */ DWORD dwTimeout) = 0;
-
+
virtual HRESULT STDMETHODCALLTYPE EndConfiguration( void) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE RequestBuffers(
+
+ virtual HRESULT STDMETHODCALLTYPE RequestBuffers(
/* [in] */ DWORD dwSize,
/* [in] */ DWORD dwAlignment,
/* [in] */ DWORD dwCount) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE QueryAvailableBuffers(
+
+ virtual HRESULT STDMETHODCALLTYPE QueryAvailableBuffers(
/* [in] */ DWORD dwSize,
/* [in] */ DWORD dwAlignment,
/* [out] */ DWORD *pCount) = 0;
-
+
virtual HRESULT STDMETHODCALLTYPE ExecuteCompletionAgent( void) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE OpenDevice(
+
+ virtual HRESULT STDMETHODCALLTYPE OpenDevice(
/* [in] */ LPCWSTR lpName,
/* [out] */ IServerVirtualDevice **ppVirtualDevice) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE AllocateBuffer(
+
+ virtual HRESULT STDMETHODCALLTYPE AllocateBuffer(
/* [out] */ BYTE **ppBuffer,
/* [in] */ DWORD dwSize,
/* [in] */ DWORD dwAlignment) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE FreeBuffer(
+
+ virtual HRESULT STDMETHODCALLTYPE FreeBuffer(
/* [in] */ BYTE *pBuffer,
/* [in] */ DWORD dwSize) = 0;
-
- virtual HRESULT STDMETHODCALLTYPE IsSharedBuffer(
+
+ virtual HRESULT STDMETHODCALLTYPE IsSharedBuffer(
/* [in] */ BYTE *pBuffer) = 0;
-
+
virtual HRESULT STDMETHODCALLTYPE SignalAbort( void) = 0;
-
+
virtual HRESULT STDMETHODCALLTYPE Close( void) = 0;
-
+
};
-
+
#else /* C style interface */
typedef struct IServerVirtualDeviceSet2Vtbl
{
BEGIN_INTERFACE
-
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
+
+ HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
IServerVirtualDeviceSet2 * This,
/* [in] */ REFIID riid,
- /* [annotation][iid_is][out] */
- __RPC__deref_out void **ppvObject);
-
- ULONG ( STDMETHODCALLTYPE *AddRef )(
+ /* [iid_is][out] */ void **ppvObject);
+
+ ULONG ( STDMETHODCALLTYPE *AddRef )(
IServerVirtualDeviceSet2 * This);
-
- ULONG ( STDMETHODCALLTYPE *Release )(
+
+ ULONG ( STDMETHODCALLTYPE *Release )(
IServerVirtualDeviceSet2 * This);
-
- HRESULT ( STDMETHODCALLTYPE *Open )(
+
+ HRESULT ( STDMETHODCALLTYPE *Open )(
IServerVirtualDeviceSet2 * This,
/* [in] */ LPCWSTR lpInstanceName,
/* [in] */ LPCWSTR lpSetName);
-
- HRESULT ( STDMETHODCALLTYPE *GetConfiguration )(
+
+ HRESULT ( STDMETHODCALLTYPE *GetConfiguration )(
IServerVirtualDeviceSet2 * This,
/* [out] */ struct VDConfig *pCfg);
-
- HRESULT ( STDMETHODCALLTYPE *BeginConfiguration )(
+
+ HRESULT ( STDMETHODCALLTYPE *BeginConfiguration )(
IServerVirtualDeviceSet2 * This,
/* [in] */ DWORD dwFeatures,
/* [in] */ DWORD dwBlockSize,
/* [in] */ DWORD dwAlignment,
/* [in] */ DWORD dwMaxTransferSize,
/* [in] */ DWORD dwTimeout);
-
- HRESULT ( STDMETHODCALLTYPE *EndConfiguration )(
+
+ HRESULT ( STDMETHODCALLTYPE *EndConfiguration )(
IServerVirtualDeviceSet2 * This);
-
- HRESULT ( STDMETHODCALLTYPE *RequestBuffers )(
+
+ HRESULT ( STDMETHODCALLTYPE *RequestBuffers )(
IServerVirtualDeviceSet2 * This,
/* [in] */ DWORD dwSize,
/* [in] */ DWORD dwAlignment,
/* [in] */ DWORD dwCount);
-
- HRESULT ( STDMETHODCALLTYPE *QueryAvailableBuffers )(
+
+ HRESULT ( STDMETHODCALLTYPE *QueryAvailableBuffers )(
IServerVirtualDeviceSet2 * This,
/* [in] */ DWORD dwSize,
/* [in] */ DWORD dwAlignment,
/* [out] */ DWORD *pCount);
-
- HRESULT ( STDMETHODCALLTYPE *ExecuteCompletionAgent )(
+
+ HRESULT ( STDMETHODCALLTYPE *ExecuteCompletionAgent )(
IServerVirtualDeviceSet2 * This);
-
- HRESULT ( STDMETHODCALLTYPE *OpenDevice )(
+
+ HRESULT ( STDMETHODCALLTYPE *OpenDevice )(
IServerVirtualDeviceSet2 * This,
/* [in] */ LPCWSTR lpName,
/* [out] */ IServerVirtualDevice **ppVirtualDevice);
-
- HRESULT ( STDMETHODCALLTYPE *AllocateBuffer )(
+
+ HRESULT ( STDMETHODCALLTYPE *AllocateBuffer )(
IServerVirtualDeviceSet2 * This,
/* [out] */ BYTE **ppBuffer,
/* [in] */ DWORD dwSize,
/* [in] */ DWORD dwAlignment);
-
- HRESULT ( STDMETHODCALLTYPE *FreeBuffer )(
+
+ HRESULT ( STDMETHODCALLTYPE *FreeBuffer )(
IServerVirtualDeviceSet2 * This,
/* [in] */ BYTE *pBuffer,
/* [in] */ DWORD dwSize);
-
- HRESULT ( STDMETHODCALLTYPE *IsSharedBuffer )(
+
+ HRESULT ( STDMETHODCALLTYPE *IsSharedBuffer )(
IServerVirtualDeviceSet2 * This,
/* [in] */ BYTE *pBuffer);
-
- HRESULT ( STDMETHODCALLTYPE *SignalAbort )(
+
+ HRESULT ( STDMETHODCALLTYPE *SignalAbort )(
IServerVirtualDeviceSet2 * This);
-
- HRESULT ( STDMETHODCALLTYPE *Close )(
+
+ HRESULT ( STDMETHODCALLTYPE *Close )(
IServerVirtualDeviceSet2 * This);
-
+
END_INTERFACE
} IServerVirtualDeviceSet2Vtbl;
@@ -1299,59 +1299,59 @@ EXTERN_C const IID IID_IServerVirtualDeviceSet2;
CONST_VTBL struct IServerVirtualDeviceSet2Vtbl *lpVtbl;
};
-
+
#ifdef COBJMACROS
#define IServerVirtualDeviceSet2_QueryInterface(This,riid,ppvObject) \
- ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
+ ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
#define IServerVirtualDeviceSet2_AddRef(This) \
- ( (This)->lpVtbl -> AddRef(This) )
+ ( (This)->lpVtbl -> AddRef(This) )
#define IServerVirtualDeviceSet2_Release(This) \
- ( (This)->lpVtbl -> Release(This) )
+ ( (This)->lpVtbl -> Release(This) )
#define IServerVirtualDeviceSet2_Open(This,lpInstanceName,lpSetName) \
- ( (This)->lpVtbl -> Open(This,lpInstanceName,lpSetName) )
+ ( (This)->lpVtbl -> Open(This,lpInstanceName,lpSetName) )
#define IServerVirtualDeviceSet2_GetConfiguration(This,pCfg) \
- ( (This)->lpVtbl -> GetConfiguration(This,pCfg) )
+ ( (This)->lpVtbl -> GetConfiguration(This,pCfg) )
#define IServerVirtualDeviceSet2_BeginConfiguration(This,dwFeatures,dwBlockSize,dwAlignment,dwMaxTransferSize,dwTimeout) \
- ( (This)->lpVtbl -> BeginConfiguration(This,dwFeatures,dwBlockSize,dwAlignment,dwMaxTransferSize,dwTimeout) )
+ ( (This)->lpVtbl -> BeginConfiguration(This,dwFeatures,dwBlockSize,dwAlignment,dwMaxTransferSize,dwTimeout) )
#define IServerVirtualDeviceSet2_EndConfiguration(This) \
- ( (This)->lpVtbl -> EndConfiguration(This) )
+ ( (This)->lpVtbl -> EndConfiguration(This) )
#define IServerVirtualDeviceSet2_RequestBuffers(This,dwSize,dwAlignment,dwCount) \
- ( (This)->lpVtbl -> RequestBuffers(This,dwSize,dwAlignment,dwCount) )
+ ( (This)->lpVtbl -> RequestBuffers(This,dwSize,dwAlignment,dwCount) )
#define IServerVirtualDeviceSet2_QueryAvailableBuffers(This,dwSize,dwAlignment,pCount) \
- ( (This)->lpVtbl -> QueryAvailableBuffers(This,dwSize,dwAlignment,pCount) )
+ ( (This)->lpVtbl -> QueryAvailableBuffers(This,dwSize,dwAlignment,pCount) )
#define IServerVirtualDeviceSet2_ExecuteCompletionAgent(This) \
- ( (This)->lpVtbl -> ExecuteCompletionAgent(This) )
+ ( (This)->lpVtbl -> ExecuteCompletionAgent(This) )
#define IServerVirtualDeviceSet2_OpenDevice(This,lpName,ppVirtualDevice) \
- ( (This)->lpVtbl -> OpenDevice(This,lpName,ppVirtualDevice) )
+ ( (This)->lpVtbl -> OpenDevice(This,lpName,ppVirtualDevice) )
#define IServerVirtualDeviceSet2_AllocateBuffer(This,ppBuffer,dwSize,dwAlignment) \
- ( (This)->lpVtbl -> AllocateBuffer(This,ppBuffer,dwSize,dwAlignment) )
+ ( (This)->lpVtbl -> AllocateBuffer(This,ppBuffer,dwSize,dwAlignment) )
#define IServerVirtualDeviceSet2_FreeBuffer(This,pBuffer,dwSize) \
- ( (This)->lpVtbl -> FreeBuffer(This,pBuffer,dwSize) )
+ ( (This)->lpVtbl -> FreeBuffer(This,pBuffer,dwSize) )
#define IServerVirtualDeviceSet2_IsSharedBuffer(This,pBuffer) \
- ( (This)->lpVtbl -> IsSharedBuffer(This,pBuffer) )
+ ( (This)->lpVtbl -> IsSharedBuffer(This,pBuffer) )
#define IServerVirtualDeviceSet2_SignalAbort(This) \
- ( (This)->lpVtbl -> SignalAbort(This) )
+ ( (This)->lpVtbl -> SignalAbort(This) )
#define IServerVirtualDeviceSet2_Close(This) \
- ( (This)->lpVtbl -> Close(This) )
+ ( (This)->lpVtbl -> Close(This) )
#endif /* COBJMACROS */
@@ -1360,7 +1360,7 @@ EXTERN_C const IID IID_IServerVirtualDeviceSet2;
-HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet2_Open_Proxy(
+HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet2_Open_Proxy(
IServerVirtualDeviceSet2 * This,
/* [in] */ LPCWSTR lpInstanceName,
/* [in] */ LPCWSTR lpSetName);
@@ -1373,7 +1373,7 @@ void __RPC_STUB IServerVirtualDeviceSet2_Open_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet2_GetConfiguration_Proxy(
+HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet2_GetConfiguration_Proxy(
IServerVirtualDeviceSet2 * This,
/* [out] */ struct VDConfig *pCfg);
@@ -1385,7 +1385,7 @@ void __RPC_STUB IServerVirtualDeviceSet2_GetConfiguration_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet2_BeginConfiguration_Proxy(
+HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet2_BeginConfiguration_Proxy(
IServerVirtualDeviceSet2 * This,
/* [in] */ DWORD dwFeatures,
/* [in] */ DWORD dwBlockSize,
@@ -1401,7 +1401,7 @@ void __RPC_STUB IServerVirtualDeviceSet2_BeginConfiguration_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet2_EndConfiguration_Proxy(
+HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet2_EndConfiguration_Proxy(
IServerVirtualDeviceSet2 * This);
@@ -1412,7 +1412,7 @@ void __RPC_STUB IServerVirtualDeviceSet2_EndConfiguration_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet2_RequestBuffers_Proxy(
+HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet2_RequestBuffers_Proxy(
IServerVirtualDeviceSet2 * This,
/* [in] */ DWORD dwSize,
/* [in] */ DWORD dwAlignment,
@@ -1426,7 +1426,7 @@ void __RPC_STUB IServerVirtualDeviceSet2_RequestBuffers_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet2_QueryAvailableBuffers_Proxy(
+HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet2_QueryAvailableBuffers_Proxy(
IServerVirtualDeviceSet2 * This,
/* [in] */ DWORD dwSize,
/* [in] */ DWORD dwAlignment,
@@ -1440,7 +1440,7 @@ void __RPC_STUB IServerVirtualDeviceSet2_QueryAvailableBuffers_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet2_ExecuteCompletionAgent_Proxy(
+HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet2_ExecuteCompletionAgent_Proxy(
IServerVirtualDeviceSet2 * This);
@@ -1451,7 +1451,7 @@ void __RPC_STUB IServerVirtualDeviceSet2_ExecuteCompletionAgent_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet2_OpenDevice_Proxy(
+HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet2_OpenDevice_Proxy(
IServerVirtualDeviceSet2 * This,
/* [in] */ LPCWSTR lpName,
/* [out] */ IServerVirtualDevice **ppVirtualDevice);
@@ -1464,7 +1464,7 @@ void __RPC_STUB IServerVirtualDeviceSet2_OpenDevice_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet2_AllocateBuffer_Proxy(
+HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet2_AllocateBuffer_Proxy(
IServerVirtualDeviceSet2 * This,
/* [out] */ BYTE **ppBuffer,
/* [in] */ DWORD dwSize,
@@ -1478,7 +1478,7 @@ void __RPC_STUB IServerVirtualDeviceSet2_AllocateBuffer_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet2_FreeBuffer_Proxy(
+HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet2_FreeBuffer_Proxy(
IServerVirtualDeviceSet2 * This,
/* [in] */ BYTE *pBuffer,
/* [in] */ DWORD dwSize);
@@ -1491,7 +1491,7 @@ void __RPC_STUB IServerVirtualDeviceSet2_FreeBuffer_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet2_IsSharedBuffer_Proxy(
+HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet2_IsSharedBuffer_Proxy(
IServerVirtualDeviceSet2 * This,
/* [in] */ BYTE *pBuffer);
@@ -1503,7 +1503,7 @@ void __RPC_STUB IServerVirtualDeviceSet2_IsSharedBuffer_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet2_SignalAbort_Proxy(
+HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet2_SignalAbort_Proxy(
IServerVirtualDeviceSet2 * This);
@@ -1514,7 +1514,7 @@ void __RPC_STUB IServerVirtualDeviceSet2_SignalAbort_Stub(
DWORD *_pdwStubPhase);
-HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet2_Close_Proxy(
+HRESULT STDMETHODCALLTYPE IServerVirtualDeviceSet2_Close_Proxy(
IServerVirtualDeviceSet2 * This);
@@ -1529,21 +1529,19 @@ void __RPC_STUB IServerVirtualDeviceSet2_Close_Stub(
#endif /* __IServerVirtualDeviceSet2_INTERFACE_DEFINED__ */
-/* interface __MIDL_itf_vdi_0000_0006 */
-/* [local] */
+/* interface __MIDL_itf_vdi_0014 */
+/* [local] */
const IID CLSID_WINFS_ClientVirtualDeviceSet = {0x50b99b13, 0x7b84, 0x44d3, {0x9c, 0x98, 0x26, 0x52, 0xbf, 0x14, 0x4d, 0x96}};
const IID CLSID_WINFS_ServerVirtualDeviceSet = {0x92e6b26b, 0x57da, 0x47ed, {0x81, 0x53, 0xdf, 0xf1, 0x87, 0xa7, 0x6d, 0xde}};
-const IID CLSID_WIDVDI_ClientVirtualDeviceSet = {0xa9a3fe12, 0x61c7, 0x496e, { 0xaa, 0xbf, 0xb8, 0x3e, 0x32, 0x87, 0xab, 0x82 }};
-const IID CLSID_WIDVDI_ServerVirtualDeviceSet = {0xa6f16b19, 0x40, 0x4947, { 0x8d, 0x3f, 0x52, 0xde, 0x12, 0x9b, 0xb6, 0x15 }};
#define CLSID_MSSQL_ClientVirtualDeviceSet IID_IClientVirtualDeviceSet
#define CLSID_MSSQL_ServerVirtualDeviceSet IID_IServerVirtualDeviceSet
#pragma pack(pop, _vdi_h_)
-extern RPC_IF_HANDLE __MIDL_itf_vdi_0000_0006_v0_0_c_ifspec;
-extern RPC_IF_HANDLE __MIDL_itf_vdi_0000_0006_v0_0_s_ifspec;
+extern RPC_IF_HANDLE __MIDL_itf_vdi_0014_v0_0_c_ifspec;
+extern RPC_IF_HANDLE __MIDL_itf_vdi_0014_v0_0_s_ifspec;
/* Additional Prototypes for ALL interfaces */
diff --git a/samples/features/sqlvdi/vdierror.h b/samples/features/sqlvdi/include/vdierror.h
similarity index 100%
rename from samples/features/sqlvdi/vdierror.h
rename to samples/features/sqlvdi/include/vdierror.h
diff --git a/samples/features/sqlvdi/vdiguid.h b/samples/features/sqlvdi/include/vdiguid.h
similarity index 83%
rename from samples/features/sqlvdi/vdiguid.h
rename to samples/features/sqlvdi/include/vdiguid.h
index 0a034566fc..986f35902d 100644
--- a/samples/features/sqlvdi/vdiguid.h
+++ b/samples/features/sqlvdi/include/vdiguid.h
@@ -1,11 +1,22 @@
+
+/* this ALWAYS GENERATED file contains the IIDs and CLSIDs */
+
/* link this file in with the server and any clients */
-/* File created by MIDL compiler version 7.00.0408 */
+ /* File created by MIDL compiler version 7.00.0408 */
/* at Tue Sep 28 18:18:04 2004
*/
-
+/* Compiler settings for vdi.idl:
+ Oicf, W1, Zp8, env=Win32 (32b run)
+ protocol : dce , ms_ext, c_ext, robust
+ error checks: allocation ref bounds_check enum stub_data
+ VC __declspec() decoration level:
+ __declspec(uuid()), __declspec(selectany), __declspec(novtable)
+ DECLSPEC_UUID(), MIDL_INTERFACE()
+*/
+//@@MIDL_FILE_HEADING( )
#if !defined(_M_IA64) && !defined(_M_AMD64)
@@ -15,7 +26,7 @@
#ifdef __cplusplus
extern "C"{
-#endif
+#endif
#include
@@ -99,8 +110,8 @@ MIDL_DEFINE_GUID(IID, IID_IServerVirtualDeviceSet2,0xAECBD0D6,0x24C6,0x11d3,0x85
/* Compiler settings for vdi.idl:
Oicf, W1, Zp8, env=Win64 (32b run,appending)
protocol : dce , ms_ext, c_ext, robust
- error checks: allocation ref bounds_check enum stub_data
- VC __declspec() decoration level:
+ error checks: allocation ref bounds_check enum stub_data
+ VC __declspec() decoration level:
__declspec(uuid()), __declspec(selectany), __declspec(novtable)
DECLSPEC_UUID(), MIDL_INTERFACE()
*/
@@ -114,7 +125,7 @@ MIDL_DEFINE_GUID(IID, IID_IServerVirtualDeviceSet2,0xAECBD0D6,0x24C6,0x11d3,0x85
#ifdef __cplusplus
extern "C"{
-#endif
+#endif
#include
diff --git a/samples/features/sqlvdi/mprocess/mprocess.cpp b/samples/features/sqlvdi/mprocess/mprocess.cpp
new file mode 100644
index 0000000000..a4c6ae22b4
--- /dev/null
+++ b/samples/features/sqlvdi/mprocess/mprocess.cpp
@@ -0,0 +1,661 @@
+/***********************************************************************
+Copyright (c) Microsoft Corporation
+All Rights Reserved.
+***********************************************************************/
+// This source code is an intended supplement to the Microsoft SQL
+// Server online references and related electronic documentation.
+//
+// This sample is for instructional purposes only.
+// Code contained herein is not intended to be used "as is" in real applications.
+//
+// mprocess.cpp :
+//
+// Test & demonstrate the use of multiple streams where each stream is
+// handled by a secondary process.
+//
+// This is a sample program used to demonstrate the Virtual Device Interface
+// feature of Microsoft SQL Server.
+//
+// The program will backup or restore the 'pubs' sample database.
+//
+// The program requires two command line parameters.
+// 1)
+// One of:
+// b perform a backup
+// r perform a restore
+//
+// s Act as a secondary client (used internally only)
+//
+// 2)
+// If b or r is given, then a second parm gives the number of streams to use,
+// (1-32).
+// The secondary processes are invoked automatically, and the second parm is the
+// stream id (0..31), the third parm the VDSName.
+//
+
+#define _WIN32_DCOM
+
+#include // for 'CoInitialize()'
+#include // for file operations
+#include // for toupper ()
+#include
+
+#include "vdi.h" // interface declaration
+#include "vdierror.h" // error constants
+#include "vdiguid.h" // define the GUIDs
+
+void LogError(
+ const char* location, // must always be provided
+ const char* description, // NULL is acceptable
+ DWORD errCode); // windows status code
+
+int performTransfer(
+ IClientVirtualDevice* vd,
+ int backup,
+ int streamId);
+
+HANDLE execSQL(bool doBackup, int nStreams);
+
+int runSecondary(int streamId, IClientVirtualDeviceSet2* vds);
+
+int startSecondaries(
+ IClientVirtualDeviceSet2* vds,
+ HANDLE hSQLProcess, // handle to process dealing with the SQL
+ int nStreams, // number of i/o streams
+ char* pgmName // the name of this program
+);
+
+// Using a GUID for the VDS Name is a good way to assure uniqueness.
+//
+WCHAR wVdsName[100];
+
+//
+// main function
+//
+int main(int argc, char* argv[])
+{
+ HRESULT hr;
+ IClientVirtualDeviceSet2* vds = nullptr;
+ VDConfig config;
+ bool badParm = true;
+ bool doBackup;
+ HANDLE hProcess;
+ int termCode = -1;
+ int nStreams = 1;
+ bool isSecondary = false;
+
+ // Check the input parm
+ //
+ if (argc >= 3)
+ {
+ sscanf_s(argv[2], "%d", &nStreams);
+
+ switch (toupper(argv[1][0]))
+ {
+ case 'B':
+ doBackup = true;
+ badParm = false;
+ break;
+
+ case 'R':
+ doBackup = false;
+ badParm = false;
+ break;
+
+ case 'S':
+ doBackup = false; // we don't know or care
+ badParm = false;
+ isSecondary = true;
+
+ // nStreams is the streamid!
+ swprintf_s(wVdsName, L"%hs", argv[3]);
+ break;
+ }
+ }
+
+ if (badParm)
+ {
+ printf("usage: mprocess {B|R} \n"
+ "Demonstrate a multistream Backup or Restore using the Virtual Device Interface\n");
+ exit(1);
+ }
+
+ if (isSecondary)
+ {
+ printf("Secondary pid %d working on stream %d\n", GetCurrentProcessId(), nStreams);
+ }
+ else
+ {
+ // 1..32 streams.
+ //
+ if (nStreams < 1)
+ nStreams = 1;
+ else if (nStreams > 32)
+ nStreams = 32;
+
+ printf("Performing a %s using %d virtual device(s).\n",
+ (doBackup) ? "BACKUP" : "RESTORE", nStreams);
+ }
+
+ // Initialize COM Library
+ // Note: _WIN32_DCOM must be defined during the compile.
+ //
+ hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
+
+ if (FAILED(hr))
+ {
+ printf("Coinit fails: x%X\n", hr);
+ exit(1);
+ }
+
+
+ // Get an interface to the device set.
+ // Notice how we use a single IID for both the class and interface
+ // identifiers.
+ //
+ hr = CoCreateInstance(
+ IID_IClientVirtualDeviceSet,
+ nullptr,
+ CLSCTX_INPROC_SERVER,
+ IID_IClientVirtualDeviceSet,
+ (void**)&vds);
+
+ if (FAILED(hr))
+ {
+ // This failure might happen if the DLL was not registered.
+ //
+ printf("Could not create component: x%X\n", hr);
+ printf("Check registration of SQLVDI.DLL and value of IID\n");
+ goto exit;
+ }
+
+ // Perform secondary processing, if this is a
+ // secondary process.
+ //
+ if (isSecondary)
+ {
+ termCode = runSecondary(nStreams, vds);
+
+ goto exit;
+ }
+
+ // The following logic is executed by the primary process.
+ //
+
+ // Setup the VDI configuration we want to use.
+ // This program doesn't use any fancy features, so the
+ // only field to setup is the deviceCount.
+ //
+ // The server will treat the virtual device just like a pipe:
+ // I/O will be strictly sequential with only the basic commands.
+ //
+ memset(&config, 0, sizeof(config));
+
+ config.deviceCount = nStreams;
+
+ // Create a GUID to use for a unique virtual device name
+ //
+ GUID vdsId;
+ CoCreateGuid(&vdsId);
+ StringFromGUID2(vdsId, wVdsName, 49);
+
+ // Create the virtual device set
+ // for use by the default instance.
+ //
+ // To use a named instance, change the
+ // first parameter in CreateEx to your instance's name.
+ //
+ hr = vds->CreateEx(nullptr, wVdsName, &config);
+ if (FAILED(hr))
+ {
+ printf("VDS::Create fails: x%X", hr);
+ goto exit;
+ }
+
+ // Send the SQL command, via isql in a subprocess.
+ //
+ printf("\nSending the SQL...\n");
+
+ hProcess = execSQL(doBackup, nStreams);
+ if (hProcess == nullptr)
+ {
+ printf("execSQL failed.\n");
+ goto shutdown;
+ }
+
+
+ // Wait for the server to connect, completing the configuration.
+ // Notice that we wait a maximum of 15 seconds.
+ //
+ printf("\nWaiting for SQL to complete configuration...\n");
+
+ hr = vds->GetConfiguration(15000, &config);
+ if (FAILED(hr))
+ {
+ printf("VDS::Getconfig fails: x%X\n", hr);
+ goto shutdown;
+ }
+
+ // Handle the virtual devices in secondary processes.
+ //
+ printf("\nSpawning secondary processes...\n");
+ termCode = startSecondaries(vds, hProcess, nStreams, argv[0]);
+
+shutdown:
+
+ // Close the set
+ //
+ vds->Close();
+
+ // COM reference counting: Release the interface.
+ //
+ vds->Release();
+
+exit:
+
+ // Uninitialize COM Library
+ //
+ CoUninitialize();
+
+ return termCode;
+}
+
+//
+// Execute a basic backup/restore, by starting 'osql' in a subprocess.
+//
+// Returns:
+// NULL : failed to start isql
+// else : process handle
+//
+HANDLE execSQL(bool doBackup, int nStreams)
+{
+ WCHAR cmd[5000];
+ WCHAR extend[100];
+ PROCESS_INFORMATION pi;
+ STARTUPINFO si;
+ int ix;
+
+ // Build the SQL, submitting it via 'isql'
+ // If you want to use Windows NT Authentication, please do not use the -U or -P options.
+ //
+ // To use a named instance, change "-S ." to "-S .\\instance_name"
+ //
+ swprintf_s(cmd, L"osql -S . -E -b -Q\"%s DATABASE PUBS %s VIRTUAL_DEVICE='%ls'",
+ (doBackup) ? L"BACKUP" : L"RESTORE",
+ (doBackup) ? L"TO" : L"FROM",
+ wVdsName);
+
+ for (ix = 1; ix < nStreams; ix++)
+ {
+ swprintf_s(extend, L", VIRTUAL_DEVICE='%ls%d'", wVdsName, ix);
+ wcscat_s(cmd, extend);
+ }
+
+ wcscat_s(cmd, L"\"");
+
+ wprintf(L"Submitting SQL:\n%s\n\n", cmd);
+
+ // use my process for startup info
+ //
+ GetStartupInfo(&si);
+
+ if (!CreateProcess(nullptr, cmd, nullptr, nullptr,
+ true, // inherit handles (stdin/stdout)
+ 0, // creation flags,
+ nullptr, nullptr,
+ &si, // startup info
+ &pi)) // out: process info
+ {
+ LogError("startSecondary", "CreateProcess", GetLastError());
+ return nullptr;
+ }
+
+ CloseHandle(pi.hThread);
+
+ // Return the process handle
+ //
+ return (pi.hProcess);
+}
+
+//-----------------------------------------------------------
+// Invoke the secondary processes, and wait for all children
+// to complete.
+//
+// Returns: 0 if no errors were detected.
+//
+//
+int startSecondaries(
+ IClientVirtualDeviceSet2* vds,
+ HANDLE hSQLProcess, // handle to process dealing with the SQL
+ int nStreams, // number of i/o streams
+ char* pgmName // the name of this program
+)
+{
+ int ix, nActive;
+ HANDLE children[33]; // 32 is maximum number of streams.
+
+ // plus one for the isql process.
+ DWORD waitStatus, exitCode;
+ WCHAR cmd[200];
+ PROCESS_INFORMATION pi;
+ STARTUPINFO si;
+
+ // use my process for startup info
+ //
+ GetStartupInfo(&si);
+
+ for (ix = 0; ix < nStreams; ix++)
+ {
+ swprintf_s(cmd, L"%hs s %d %ls", pgmName, ix, wVdsName);
+
+ if (!CreateProcess(nullptr, cmd, nullptr, nullptr,
+ true, // inherit handles (just stdin/stdout I hope!)
+ 0, // creation flags,
+ nullptr, nullptr,
+ &si, // startup info
+ &pi)) // out: process info
+ {
+ wprintf(L"Error starting %s\n", cmd);
+ LogError("startSecondary", "CreateProcess", GetLastError());
+ goto errorExit;
+ }
+ // keep the process handle
+ children[ix] = pi.hProcess;
+
+ CloseHandle(pi.hThread);
+ }
+
+ // Add the isql process into the array
+ //
+ children[nStreams] = hSQLProcess;
+ nActive = nStreams + 1;
+
+ // Wait for all to finish.
+ // Max wait is one minute for this tiny test.
+ //
+ printf("All children are now running.\n"
+ "Waiting for their completion...\n");
+
+ // Notice how this differs from the threaded model in mthread.cpp.
+ // In the multiprocess model, the primary client (running this code)
+ // is responsible for detecting abnormal termination of the
+ // secondary clients.
+ // A simple "wait-for-all" approach may wait indefinitely if only
+ // one of the secondaries was to abnormally terminate.
+ //
+ do
+ {
+ // Wait for any completion
+ //
+ waitStatus = WaitForMultipleObjects(nActive, children,
+ FALSE, INFINITE);
+
+ if (waitStatus >= WAIT_OBJECT_0 &&
+ waitStatus < WAIT_OBJECT_0 + nActive)
+ {
+ // One of the children completed.
+ // Determine which one.
+ //
+ ix = waitStatus - WAIT_OBJECT_0;
+
+ // Check its completion code
+ //
+ if (!GetExitCodeProcess(children[ix], &exitCode))
+ {
+ LogError("startSecondary", "GetExitCode", GetLastError());
+ goto errorExit;
+ }
+
+ if (exitCode != 0)
+ {
+ printf("A child exitted with code %d\n", exitCode);
+ goto errorExit;
+ }
+
+ // It is good programming practice to close handles when
+ // finished with them.
+ // Since this sample simply terminates the process for error
+ // handling, we don't need to do it, as handles are automatically
+ // closed as part of process termination.
+ //
+ CloseHandle(children[ix]);
+
+ // Remove the handle for this child
+ //
+ memmove(&children[ix], &children[ix + 1],
+ sizeof(HANDLE) * (nActive - ix - 1));
+
+ nActive--;
+
+ }
+ else
+ {
+ printf("Unexpected wait code: %d\n", waitStatus);
+ goto errorExit;
+ }
+
+ } while (nActive > 0);
+
+ printf("All children completed successfully\n");
+
+ return 0;
+
+errorExit:
+ // Handle all problems in a trivial fashion:
+ // SignalAbort() will cause all processes using the virtual device set
+ // to terminate processing.
+ // Thus, we don't bother waiting for any children to terminate.
+ //
+ vds->SignalAbort();
+ return -1;
+
+}
+
+//------------------------------------------------------------------
+// Perform secondary client processing
+// Return 0 if no errors detected, else nonzero.
+//
+int runSecondary(int streamId, IClientVirtualDeviceSet2* vds)
+{
+ HRESULT hr;
+ WCHAR devName[100];
+ IClientVirtualDevice* vd;
+ VDConfig config;
+ int termCode;
+
+ // Open the device
+ //
+ if (streamId == 0)
+ {
+ // The first device has the same name as the set.
+ //
+ wcscpy_s(devName, wVdsName);
+ }
+ else
+ {
+ // For this example, we've simply appended a number
+ // for additional devices. You are free to name them
+ // as you wish.
+ //
+ swprintf_s(devName, L"%ls%d", wVdsName, streamId);
+ }
+
+ // Open the virtual device set in this secondary process.
+ //
+ // To use a named instance, change the
+ // first parameter in OpenInSecondaryEx to your instance's name.
+ //
+ hr = vds->OpenInSecondaryEx(nullptr, wVdsName);
+ if (FAILED(hr))
+ {
+ wprintf(L"VD::Open(%ls) fails: x%X", devName, hr);
+ return -1;
+ }
+
+ // Open the device assigned to this process.
+ //
+ hr = vds->OpenDevice(devName, &vd);
+ if (FAILED(hr))
+ {
+ wprintf(L"OpenDevice fails on %ls: x%X", devName, hr);
+ return -1;
+ }
+
+ // Grab the config to figure out data direction
+ //
+ hr = vds->GetConfiguration(INFINITE, &config);
+ if (FAILED(hr))
+ {
+ wprintf(L"VDS::Getconfig fails: x%X\n", hr);
+ termCode = -1;
+ goto errExit;
+ }
+
+ printf("\nPerforming data transfer...\n");
+
+ termCode = performTransfer(vd,
+ (config.features & VDF_WriteMedia), streamId);
+
+errExit:
+
+ // If errors were detected, force an abort.
+ //
+ if (termCode != 0)
+ {
+ vds->SignalAbort();
+ }
+
+ vds->Close();
+
+ return termCode;
+}
+
+
+// This routine reads commands from the server until a 'Close' status is received.
+// It simply synchronously reads or writes a file on the root of the current drive.
+//
+// Returns 0, if no errors are detected, else non-zero.
+//
+int performTransfer(
+ IClientVirtualDevice* vd,
+ int backup,
+ int streamId)
+{
+ FILE* fh;
+ char fname[80];
+ VDC_Command* cmd;
+ DWORD completionCode;
+ DWORD bytesTransferred;
+ HRESULT hr;
+ int termCode = -1;
+
+ sprintf_s(fname, "multi.%d.dmp", streamId);
+
+ errno_t error = fopen_s(&fh, fname, (backup) ? "wb" : "rb");
+ if (error != 0)
+ {
+ printf("Failed to open: %s\n", fname);
+ return -1;
+ }
+
+ while (SUCCEEDED(hr = vd->GetCommand(INFINITE, &cmd)))
+ {
+ bytesTransferred = 0;
+ switch (cmd->commandCode)
+ {
+ case VDC_Read:
+ bytesTransferred = fread(cmd->buffer, 1, cmd->size, fh);
+ if (bytesTransferred == cmd->size)
+ completionCode = ERROR_SUCCESS;
+ else
+ // assume failure is eof
+ completionCode = ERROR_HANDLE_EOF;
+
+ break;
+
+ case VDC_Write:
+ bytesTransferred = fwrite(cmd->buffer, 1, cmd->size, fh);
+ if (bytesTransferred == cmd->size)
+ {
+ completionCode = ERROR_SUCCESS;
+ }
+ else
+ // assume failure is disk full
+ completionCode = ERROR_DISK_FULL;
+ break;
+
+ case VDC_Flush:
+ fflush(fh);
+ completionCode = ERROR_SUCCESS;
+ break;
+
+ case VDC_ClearError:
+ completionCode = ERROR_SUCCESS;
+ break;
+
+ default:
+ // If command is unknown...
+ completionCode = ERROR_NOT_SUPPORTED;
+ }
+
+
+ hr = vd->CompleteCommand(cmd, completionCode, bytesTransferred, 0);
+ if (FAILED(hr))
+ {
+ printf("Completion Failed: x%X\n", hr);
+ break;
+ }
+ }
+
+ if (hr != VD_E_CLOSE)
+ {
+ printf("Unexpected termination: x%X\n", hr);
+ }
+ else
+ {
+ // As far as the data transfer is concerned, no
+ // errors occurred. The code which issues the SQL
+ // must determine if the backup/restore was
+ // really successful.
+ //
+ printf("Successfully completed data transfer.\n");
+ termCode = 0;
+ }
+
+ fclose(fh);
+
+ return termCode;
+}
+
+//--------------------------------------------------------------------
+//
+// A simple error logger.
+//
+void LogError(
+ const char* location, // must always be provided
+ const char* description, // NULL is acceptable
+ DWORD errCode) // windows status code
+{
+ LPWSTR lpMsgBuf = nullptr;
+
+ printf(
+ "Error at %s: %s StatusCode: %X\n",
+ location,
+ (description == nullptr) ? "" : description,
+ errCode);
+
+ // Attempt to explain the code
+ //
+ if (errCode != 0 && FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ nullptr,
+ errCode,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+ reinterpret_cast(&lpMsgBuf), 0, nullptr))// Process any inserts in lpMsgBuf.
+ {
+ printf("Explanation: %ls\n", lpMsgBuf);
+ LocalFree(lpMsgBuf);
+ }
+}
+
diff --git a/samples/features/sqlvdi/mprocess/mprocess.vcxproj b/samples/features/sqlvdi/mprocess/mprocess.vcxproj
new file mode 100644
index 0000000000..cb17bce2e0
--- /dev/null
+++ b/samples/features/sqlvdi/mprocess/mprocess.vcxproj
@@ -0,0 +1,141 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 17.0
+ Win32Proj
+ {fe97585b-08bd-4837-a5ba-5c960e591f29}
+ mprocess
+ 10.0
+
+
+
+ Application
+ true
+ v143
+ Unicode
+
+
+ Application
+ false
+ v143
+ true
+ Unicode
+
+
+ Application
+ true
+ v143
+ Unicode
+
+
+ Application
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Level3
+ true
+ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+ Level3
+ true
+ _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ ..\include
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/features/sqlvdi/mprocess/mprocess.vcxproj.filters b/samples/features/sqlvdi/mprocess/mprocess.vcxproj.filters
new file mode 100644
index 0000000000..6284fbcad1
--- /dev/null
+++ b/samples/features/sqlvdi/mprocess/mprocess.vcxproj.filters
@@ -0,0 +1,33 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Source Files
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
\ No newline at end of file
diff --git a/samples/features/sqlvdi/mthread/mthread.cpp b/samples/features/sqlvdi/mthread/mthread.cpp
new file mode 100644
index 0000000000..6dddbf385f
--- /dev/null
+++ b/samples/features/sqlvdi/mthread/mthread.cpp
@@ -0,0 +1,608 @@
+/***********************************************************************
+Copyright (c) Microsoft Corporation
+All Rights Reserved.
+***********************************************************************/
+// This source code is an intended supplement to the Microsoft SQL
+// Server online references and related electronic documentation.
+//
+// This sample is for instructional purposes only.
+// Code contained herein is not intended to be used "as is" in real applications.
+//
+// mthread.cpp :
+//
+// Test & demonstrate the use of multiple streams from a single process.
+//
+// This is a sample program used to demonstrate the Virtual Device Interface
+// feature of Microsoft SQL Server.
+//
+// The program will backup or restore the 'pubs' sample database.
+//
+// The program requires two command line parameters.
+// 1)
+// One of:
+// b perform a backup
+// r perform a restore
+//
+// 2)
+// The number of streams to use, 1-32.
+//
+
+#define _WIN32_DCOM
+
+#include // for 'CoInitialize()'
+#include // for file operations
+#include // for toupper ()
+#include // for C library-safe _beginthreadex,_endthreadex
+#include
+
+#include "vdi.h" // interface declaration
+#include "vdierror.h" // error constants
+#include "vdiguid.h" // define the GUIDs
+
+void LogError(
+ const char* location, // must always be provided
+ const char* description, // NULL is acceptable
+ DWORD errCode); // windows status code
+
+int performTransfer(
+ IClientVirtualDevice* vd,
+ int backup,
+ int streamId);
+
+HANDLE execSQL(bool doBackup, int nStreams);
+
+int startSecondaries(
+ IClientVirtualDeviceSet2* vds,
+ HANDLE hSQLProcess, // handle to process dealing with the SQL
+ int nStreams); // number of i/o streams
+
+unsigned __stdcall
+runSecondary(void* parms);
+
+// Using a GUID for the VDS Name is a good way to assure uniqueness.
+//
+WCHAR wVdsName[50];
+
+//
+// main function
+//
+int main(int argc, char* argv[])
+{
+ HRESULT hr;
+ IClientVirtualDeviceSet2* vds = nullptr;
+ VDConfig config;
+ bool badParm = true;
+ bool doBackup;
+ HANDLE hProcess;
+ int termCode = -1;
+ int nStreams = 1;
+
+ // Check the input parm
+ //
+ if (argc == 3)
+ {
+ sscanf_s(argv[2], "%d", &nStreams);
+
+ switch (toupper(argv[1][0]))
+ {
+ case 'B':
+ doBackup = true;
+ badParm = false;
+ break;
+
+ case 'R':
+ doBackup = false;
+ badParm = false;
+ break;
+ }
+ }
+
+ if (badParm)
+ {
+ printf("usage: mthread {B|R} \n"
+ "Demonstrate a multistream Backup or Restore using the Virtual Device Interface\n");
+ exit(1);
+ }
+
+ // 1..32 streams.
+ //
+ if (nStreams < 1)
+ nStreams = 1;
+ else if (nStreams > 32)
+ nStreams = 32;
+
+ printf("Performing a %s using %d virtual device(s).\n",
+ (doBackup) ? "BACKUP" : "RESTORE", nStreams);
+
+ // Initialize COM Library
+ // Note: _WIN32_DCOM must be defined during the compile.
+ //
+ hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
+
+ if (FAILED(hr))
+ {
+ printf("Coinit fails: x%X\n", hr);
+ exit(1);
+ }
+
+
+ // Get an interface to the device set.
+ // Notice how we use a single IID for both the class and interface
+ // identifiers.
+ //
+ hr = CoCreateInstance(
+ CLSID_MSSQL_ClientVirtualDeviceSet,
+ nullptr,
+ CLSCTX_INPROC_SERVER,
+ IID_IClientVirtualDeviceSet2,
+ (void**)&vds);
+
+ if (FAILED(hr))
+ {
+ // This failure might happen if the DLL was not registered.
+ //
+ printf("Could not create component: x%X\n", hr);
+ printf("Check registration of SQLVDI.DLL and value of IID\n");
+ goto exit;
+ }
+
+ // Setup the VDI configuration we want to use.
+ // This program doesn't use any fancy features, so the
+ // only field to setup is the deviceCount.
+ //
+ // The server will treat the virtual device just like a pipe:
+ // I/O will be strictly sequential with only the basic commands.
+ //
+ memset(&config, 0, sizeof(config));
+ config.deviceCount = nStreams;
+
+ // Create a GUID to use for a unique virtual device name
+ //
+ GUID vdsId;
+ CoCreateGuid(&vdsId);
+ StringFromGUID2(vdsId, wVdsName, 49);
+
+ // Create the virtual device set
+ // for use by the default instance.
+ //
+ // To use a named instance, change the
+ // first parameter in CreateEx to your instance's name.
+ //
+ hr = vds->CreateEx(nullptr, wVdsName, &config);
+ if (FAILED(hr))
+ {
+ printf("VDS::Create fails: x%X", hr);
+ goto exit;
+ }
+
+ // Send the SQL command, via isql in a subprocess.
+ //
+ printf("\nSending the SQL...\n");
+
+ hProcess = execSQL(doBackup, nStreams);
+ if (hProcess == nullptr)
+ {
+ printf("execSQL failed.\n");
+ goto shutdown;
+ }
+
+
+ // Wait for the server to connect, completing the configuration.
+ // Notice that we wait a maximum of 15 seconds.
+ //
+ printf("\nWaiting for SQL to complete configuration...\n");
+
+ hr = vds->GetConfiguration(15000, &config);
+ if (FAILED(hr))
+ {
+ printf("VDS::Getconfig fails: x%X\n", hr);
+ goto shutdown;
+ }
+
+ // Handle the virtual devices in secondary processes.
+ //
+ printf("\nSpawning secondary threads..\n");
+ termCode = startSecondaries(vds, hProcess, nStreams);
+
+shutdown:
+
+ // Close the set
+ //
+ vds->Close();
+
+ // COM reference counting: Release the interface.
+ //
+ vds->Release();
+
+exit:
+
+ // Uninitialize COM Library
+ //
+ CoUninitialize();
+
+ return termCode;
+}
+
+//
+// Execute a basic backup/restore, by starting 'isql' in a subprocess.
+//
+// Returns:
+// NULL : failed to start isql
+// else : process handle
+//
+HANDLE execSQL(bool doBackup, int nStreams)
+{
+ WCHAR cmd[5000];
+ WCHAR extend[100];
+ PROCESS_INFORMATION pi;
+ STARTUPINFO si;
+ int ix;
+
+ // To use a named instance, change "-S ." to "-S .\\instance_name"
+ swprintf_s(cmd, L"osql -S . -E -b -Q\"%s DATABASE PUBS %s VIRTUAL_DEVICE='%ls'",
+ (doBackup) ? L"BACKUP" : L"RESTORE",
+ (doBackup) ? L"TO" : L"FROM",
+ wVdsName);
+
+ for (ix = 1; ix < nStreams; ix++)
+ {
+ swprintf_s(extend, L", VIRTUAL_DEVICE='%ls%d'", wVdsName, ix);
+ wcscat_s(cmd, extend);
+ }
+
+ wcscat_s(cmd, L"\"");
+
+ wprintf(L"Submitting SQL:\n%s\n\n", cmd);
+
+ // use my process for startup info
+ //
+ GetStartupInfo(&si);
+
+ if (!CreateProcess(nullptr, cmd, nullptr, nullptr,
+ true, // inherit handles (stdin/stdout)
+ 0, // creation flags,
+ nullptr, nullptr,
+ &si, // startup info
+ &pi)) // out: process info
+ {
+ LogError("startSecondary", "CreateProcess", GetLastError());
+ return nullptr;
+ }
+
+ CloseHandle(pi.hThread);
+
+ // Return the process handle
+ //
+ return (pi.hProcess);
+}
+
+//-----------------------------------------------------------
+//
+// A parmameter block to use when spawning secondaries.
+//
+struct THREAD_PARMS {
+ IClientVirtualDeviceSet* vds;
+ int streamId;
+};
+
+
+//-----------------------------------------------------------
+// Invoke the secondary threads, and wait for all children
+// to complete.
+//
+// Returns: 0 if no errors were detected.
+//
+//
+int
+startSecondaries(
+ IClientVirtualDeviceSet2* vds,
+ HANDLE hSQLProcess, // handle to process dealing with the SQL
+ int nStreams // number of i/o streams
+)
+{
+ THREAD_PARMS parms[32]; // each thread needs its own parm block
+ int ix, nActive;
+ HANDLE children[33]; // 32 is maximum number of streams.
+
+ // plus one for the isql process.
+ DWORD waitStatus, exitCode;
+ unsigned threadId;
+
+ for (ix = 0; ix < nStreams; ix++)
+ {
+ // All threads share the same virtual device set,
+ // but must operate on different virtual devices.
+ //
+ parms[ix].vds = vds;
+ parms[ix].streamId = ix;
+
+ children[ix] = (HANDLE)_beginthreadex(
+ nullptr, 0, runSecondary, (void*)&parms[ix], 0, &threadId);
+
+ if (children[ix] == nullptr)
+ {
+ printf("Failed to create thread. errno is %d\n", errno);
+ goto errorExit;
+ }
+
+ printf("\nStarted thread %d\n", threadId);
+ }
+
+ // Add the isql process into the array
+ //
+ children[nStreams] = hSQLProcess;
+ nActive = nStreams + 1;
+
+ // Wait for all to finish.
+ // Max wait is one minute for this tiny test.
+ //
+ printf("All children are now running.\n"
+ "Waiting for their completion...\n");
+
+ waitStatus = WaitForMultipleObjects(nActive, children,
+ true, 60000);
+
+ if (waitStatus < WAIT_OBJECT_0 ||
+ waitStatus >= WAIT_OBJECT_0 + nActive)
+ {
+ LogError("startSecondary", "WaitForMultiple", GetLastError());
+ printf("Unexpected wait code: %d\n", waitStatus);
+ goto errorExit;
+ }
+
+ // All of the children have completed.
+ // Get the completion code from 'isql' to check for sucess.
+ //
+ if (!GetExitCodeProcess(hSQLProcess, &exitCode))
+ {
+ LogError("startSecondary", "GetExitCode", GetLastError());
+ goto errorExit;
+ }
+
+ if (exitCode != 0)
+ {
+ printf("The SQL operation failed with code %d\n", exitCode);
+ goto errorExit;
+ }
+
+ printf("The SQL operation was sucessful.\n");
+
+ // Be sure to close handles when finished with them.
+ //
+ // Notice that in our trivial error handling here we
+ // don't bother closing them, since handles are
+ // automatically closed as part of process termination.
+ //
+ for (ix = 0; ix < nActive; ix++)
+ {
+ CloseHandle(children[ix]);
+ }
+
+ return 0;
+
+errorExit:
+ // Handle all problems in a trivial fashion:
+ // SignalAbort() will cause all processes using the virtual device set
+ // to terminate processing.
+ //
+ vds->SignalAbort();
+
+ // However, since the threads are using the virtual device set allocated
+ // by the main thread, we can't let the main thread close the set before
+ // the threads are finished with it. There are two options:
+ // 1) exit the process immediately.
+ // 2) wait for the threads to terminate.
+ //
+ // Option 2 is "cleaner" but requires more code, so we just exit here:
+ //
+ ExitProcess((unsigned)-1);
+
+ return -1; // ExitProcess doesn't return; this avoids a compiler error.
+}
+
+//------------------------------------------------------------------
+// Perform secondary client processing from within a thread.
+// Exits with 0 if no errors detected, else nonzero.
+// The caller must setup a THREAD_PARMS parameter block when
+// spawning the thread.
+//
+unsigned __stdcall
+runSecondary(void* parms)
+{
+ HRESULT hr;
+ WCHAR devName[100];
+ IClientVirtualDevice* vd;
+ VDConfig config;
+ int termCode;
+
+ // Fetch the input parms
+ //
+ int streamId = ((THREAD_PARMS*)parms)->streamId;
+ IClientVirtualDeviceSet* vds = ((THREAD_PARMS*)parms)->vds;
+
+ // Build the name of the device assigned to this thread.
+ //
+ if (streamId == 0)
+ {
+ // The first device has the same name as the set.
+ //
+ wcscpy_s(devName, wVdsName);
+ }
+ else
+ {
+ // For this example, we've simply appended a number
+ // for additional devices. You are free to name them
+ // as you wish.
+ //
+ swprintf_s(devName, L"%s%d", wVdsName, streamId);
+ }
+
+ // Open the device assigned to this thread.
+ //
+ hr = vds->OpenDevice(devName, &vd);
+ if (FAILED(hr))
+ {
+ wprintf(L"OpenDevice fails on %ls: x%X", devName, hr);
+ termCode = -1;
+ goto errExit;
+ }
+
+ // Grab the config to figure out data direction
+ //
+ hr = vds->GetConfiguration(INFINITE, &config);
+ if (FAILED(hr))
+ {
+ wprintf(L"VDS::Getconfig fails: x%X\n", hr);
+ termCode = -1;
+ goto errExit;
+ }
+
+ printf("\nPerforming data transfer...\n");
+
+ termCode = performTransfer(vd,
+ (config.features & VDF_WriteMedia), streamId);
+
+errExit:
+
+ // If errors were detected, force an abort.
+ //
+ if (termCode != 0)
+ {
+ vds->SignalAbort();
+ }
+
+ return ((unsigned)termCode);
+
+}
+
+
+
+// This routine reads commands from the server until a 'Close' status is received.
+// It simply synchronously reads or writes a file on the root of the current drive.
+//
+// Returns 0, if no errors are detected, else non-zero.
+//
+int performTransfer(
+ IClientVirtualDevice* vd,
+ int backup,
+ int streamId)
+{
+ FILE* fh;
+ char fname[80];
+ VDC_Command* cmd;
+ DWORD completionCode;
+ DWORD bytesTransferred;
+ HRESULT hr;
+ int termCode = -1;
+
+ sprintf_s(fname, "multi.%d.dmp", streamId);
+
+ errno_t error = fopen_s(&fh, fname, (backup) ? "wb" : "rb");
+ if (error != 0)
+ {
+ printf("Failed to open: %s\n", fname);
+ return -1;
+ }
+
+ while (SUCCEEDED(hr = vd->GetCommand(INFINITE, &cmd)))
+ {
+ bytesTransferred = 0;
+ switch (cmd->commandCode)
+ {
+ case VDC_Read:
+ bytesTransferred = fread(cmd->buffer, 1, cmd->size, fh);
+ if (bytesTransferred == cmd->size)
+ completionCode = ERROR_SUCCESS;
+ else
+ // assume failure is eof
+ completionCode = ERROR_HANDLE_EOF;
+
+ break;
+
+ case VDC_Write:
+ bytesTransferred = fwrite(cmd->buffer, 1, cmd->size, fh);
+ if (bytesTransferred == cmd->size)
+ {
+ completionCode = ERROR_SUCCESS;
+ }
+ else
+ // assume failure is disk full
+ completionCode = ERROR_DISK_FULL;
+ break;
+
+ case VDC_Flush:
+ fflush(fh);
+ completionCode = ERROR_SUCCESS;
+ break;
+
+ case VDC_ClearError:
+ completionCode = ERROR_SUCCESS;
+ break;
+
+ default:
+ // If command is unknown...
+ completionCode = ERROR_NOT_SUPPORTED;
+ }
+
+
+ hr = vd->CompleteCommand(cmd, completionCode, bytesTransferred, 0);
+ if (FAILED(hr))
+ {
+ printf("Completion Failed: x%X\n", hr);
+ break;
+ }
+ }
+
+ if (hr != VD_E_CLOSE)
+ {
+ printf("Unexpected termination: x%X\n", hr);
+ }
+ else
+ {
+ // As far as the data transfer is concerned, no
+ // errors occurred. The code which issues the SQL
+ // must determine if the backup/restore was
+ // really successful.
+ //
+ printf("Successfully completed data transfer.\n");
+ termCode = 0;
+ }
+
+ fclose(fh);
+
+ return termCode;
+}
+
+
+//--------------------------------------------------------------------
+//
+// A simple error logger.
+//
+void LogError(
+ const char* location, // must always be provided
+ const char* description, // NULL is acceptable
+ DWORD errCode) // windows status code
+{
+ LPWSTR lpMsgBuf = nullptr;
+
+ printf(
+ "Error at %s: %s StatusCode: %X\n",
+ location,
+ (description == nullptr) ? "" : description,
+ errCode);
+
+ // Attempt to explain the code
+ //
+ if (errCode != 0 && FormatMessageW(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ nullptr,
+ errCode,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+ reinterpret_cast(&lpMsgBuf), 0, nullptr)) // Process any inserts in lpMsgBuf.
+ {
+ printf("Explanation: %ls\n", lpMsgBuf);
+ LocalFree(lpMsgBuf);
+ }
+}
+
diff --git a/samples/features/sqlvdi/mthread/mthread.vcxproj b/samples/features/sqlvdi/mthread/mthread.vcxproj
new file mode 100644
index 0000000000..82b3c0c2a3
--- /dev/null
+++ b/samples/features/sqlvdi/mthread/mthread.vcxproj
@@ -0,0 +1,141 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 17.0
+ Win32Proj
+ {f4600084-90ad-42b9-9809-ade6e1c06423}
+ mthread
+ 10.0
+
+
+
+ Application
+ true
+ v143
+ Unicode
+
+
+ Application
+ false
+ v143
+ true
+ Unicode
+
+
+ Application
+ true
+ v143
+ Unicode
+
+
+ Application
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Level3
+ true
+ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+ Level3
+ true
+ _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ ..\include
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/features/sqlvdi/mthread/mthread.vcxproj.filters b/samples/features/sqlvdi/mthread/mthread.vcxproj.filters
new file mode 100644
index 0000000000..606cc8a8dc
--- /dev/null
+++ b/samples/features/sqlvdi/mthread/mthread.vcxproj.filters
@@ -0,0 +1,33 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+ Source Files
+
+
+
\ No newline at end of file
diff --git a/samples/features/sqlvdi/osimple/osimple.cpp b/samples/features/sqlvdi/osimple/osimple.cpp
new file mode 100644
index 0000000000..d3f6663e4c
--- /dev/null
+++ b/samples/features/sqlvdi/osimple/osimple.cpp
@@ -0,0 +1,669 @@
+/***********************************************************************
+Copyright (c) Microsoft Corporation
+All Rights Reserved.
+***********************************************************************/
+// This source code is an intended supplement to the Microsoft SQL
+// Server online references and related electronic documentation.
+//
+// This sample is for instructional purposes only.
+// Code contained herein is not intended to be used "as is" in real applications.
+//
+// osimple.cpp
+//
+// This sample extends the "simple.cpp" sample to use an ODBC connection.
+// This is intended only as an introduction to ODBC; not as a comprehensive tutorial.
+//
+// This is a sample program used to demonstrate the Virtual Device Interface
+// feature of Microsoft SQL Server together with an ODBC connection.
+//
+// The program will backup or restore the 'pubs' sample database from
+// the default instance of sql server.
+//
+// The program accepts a single command line parameter.
+// One of:
+// b perform a backup
+// r perform a restore
+//
+
+
+// To gain access to free threaded COM, you'll need to define _WIN32_DCOM
+// before the system headers, either in the source (as in this example),
+// or when invoking the compiler (by using /D "_WIN32_DCOM")
+//
+// This sample uses Windows NT Authentication, other than mixed mode security,
+// to establish connections. If you want to use mixed mode security, please
+// set Trusted_Connection to no in the SQLDriverConnect command.
+// Make necessary change to the server name in the SQLDriverConnect command.
+
+#define _WIN32_DCOM
+
+#include // for 'CoInitialize()'
+#include // for file operations
+#include // for toupper ()
+#include // for C library-safe _beginthreadex,_endthreadex
+
+#include "vdi.h" // interface declaration
+#include "vdierror.h" // error constants
+
+#include "vdiguid.h" // define the interface identifiers.
+ // IMPORTANT: vdiguid.h can only be included in one source file.
+ //
+
+#include
+#include "sql.h"
+#include "sqlext.h"
+#include "odbcss.h"
+
+void performTransfer(
+ IClientVirtualDevice* vd,
+ int backup);
+
+HANDLE execSQL(bool doBackup);
+bool checkSQL(HANDLE);
+
+// Using a GUID for the VDS Name is a good way to assure uniqueness.
+//
+WCHAR wVdsName[50];
+
+//------------------------------------------------------------
+//
+// Mainline
+//
+int main(int argc, char* argv[])
+{
+ HRESULT hr;
+ IClientVirtualDeviceSet2* vds = nullptr;
+ IClientVirtualDevice* vd = nullptr;
+
+ VDConfig config;
+ bool badParm = true;
+ bool doBackup;
+ HANDLE hThread = nullptr;
+
+ // Check the input parm
+ //
+ if (argc == 2)
+ {
+ char param = toupper(argv[1][0]);
+
+ if (param == 'B')
+ {
+ doBackup = true;
+ badParm = false;
+ }
+ else if (param == 'R')
+ {
+ doBackup = false;
+ badParm = false;
+ }
+ }
+
+ if (badParm)
+ {
+ printf("usage: osimple {B|R}\n"
+ "Demonstrate a Backup or Restore using the Virtual Device Interface & ODBC\n");
+ exit(1);
+ }
+
+ printf("Performing a %s using a virtual device.\n",
+ (doBackup) ? "BACKUP" : "RESTORE");
+
+ // Initialize COM Library
+ // Note: _WIN32_DCOM must be defined during the compile.
+ //
+ hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
+
+ if (FAILED(hr))
+ {
+ printf("Coinit fails: x%X\n", hr);
+ exit(1);
+ }
+
+ // Get an interface to the device set.
+ // Notice how we use a single IID for both the class and interface
+ // identifiers.
+ //
+ hr = CoCreateInstance(
+ CLSID_MSSQL_ClientVirtualDeviceSet,
+ nullptr,
+ CLSCTX_INPROC_SERVER,
+ IID_IClientVirtualDeviceSet2,
+ (void**)&vds);
+
+ if (FAILED(hr))
+ {
+ // This failure might happen if the DLL was not registered,
+ // or if the application is using the wrong interface id (IID).
+ //
+ printf("Could not create component: x%X\n", hr);
+ printf("Check registration of SQLVDI.DLL and value of IID\n");
+ goto exit;
+ }
+
+ // Setup the VDI configuration we want to use.
+ // This program doesn't use any fancy features, so the
+ // only field to setup is the deviceCount.
+ //
+ // The server will treat the virtual device just like a pipe:
+ // I/O will be strictly sequential with only the basic commands.
+ //
+ memset(&config, 0, sizeof(config));
+ config.deviceCount = 1;
+
+ // Create a GUID to use for a unique virtual device name
+ //
+ GUID vdsId;
+ CoCreateGuid(&vdsId);
+ StringFromGUID2(vdsId, wVdsName, 49);
+
+ // Create the virtual device set
+ // for use by the default instance.
+ //
+ // To use a named instance, change the
+ // first parameter in CreateEx to your instance's name.
+ //
+ hr = vds->CreateEx(nullptr, wVdsName, &config);
+ if (FAILED(hr))
+ {
+ printf("VDS::Create fails: x%X", hr);
+ goto exit;
+ }
+
+ // Send the SQL command, by starting a thread to handle the ODBC
+ //
+ printf("\nSending the SQL...\n");
+
+ hThread = execSQL(doBackup);
+ if (hThread == nullptr)
+ {
+ printf("execSQL failed.\n");
+ goto shutdown;
+ }
+
+ // Wait for the server to connect, completing the configuration.
+ //
+ printf("\nWaiting for SQLServer to respond...\n");
+
+ while (FAILED(hr = vds->GetConfiguration(1000, &config)))
+ {
+ if (hr == VD_E_TIMEOUT)
+ {
+ // Check on the SQL thread
+ //
+ DWORD rc = WaitForSingleObject(hThread, 1000);
+ if (rc == WAIT_OBJECT_0)
+ {
+ printf("SQL command failed before VD transfer\n");
+ goto shutdown;
+ }
+ if (rc == WAIT_TIMEOUT)
+ {
+ continue;
+ }
+ printf("Check on SQL failed: %d\n", rc);
+ goto shutdown;
+ }
+
+ printf("VDS::Getconfig fails: x%X\n", hr);
+ goto shutdown;
+ }
+
+ // Open the single device in the set.
+ //
+ hr = vds->OpenDevice(wVdsName, &vd);
+ if (FAILED(hr))
+ {
+ printf("VDS::OpenDevice fails: x%X\n", hr);
+ goto shutdown;
+ }
+
+ printf("\nPerforming data transfer...\n");
+
+ performTransfer(vd, doBackup);
+
+
+shutdown:
+
+ // Close the set
+ //
+ vds->Close();
+
+ // Obtain the SQL completion information
+ //
+ if (hThread != nullptr)
+ {
+ if (checkSQL(hThread))
+ printf("\nThe SQL command executed successfully.\n");
+ else
+ printf("\nThe SQL command failed.\n");
+
+ CloseHandle(hThread);
+ }
+
+ // COM reference counting: Release the interface.
+ //
+ vds->Release();
+
+exit:
+ // Uninitialize COM Library
+ //
+ CoUninitialize();
+
+ return 0;
+}
+
+//---------------------------------------------------------------------------
+// ODBC Message processing routine.
+// This is SQLServer specific, to show native message IDs, sev, state.
+//
+// The routine will watch for message 3014 in order to detect
+// a successful backup/restore operation. This is useful for
+// operations like RESTORE which can sometimes recover from
+// errors (error messages will be followed by the 3014 success message).
+//
+void ProcessMessages(
+ SQLSMALLINT handle_type, // ODBC handle type
+ SQLHANDLE handle, // ODBC handle
+ bool ConnInd, // TRUE if sucessful connection made
+ bool* pBackupSuccess) // Set TRUE if a 3014 message is seen.
+{
+ RETCODE plm_retcode = SQL_SUCCESS;
+ SQLWCHAR plm_szSqlState[SQL_SQLSTATE_SIZE + 1];
+ SQLWCHAR plm_szErrorMsg[SQL_MAX_MESSAGE_LENGTH + 1];
+ SDWORD plm_pfNativeError = 0L;
+ SWORD plm_pcbErrorMsg = 0;
+ SQLSMALLINT plm_cRecNmbr = 1;
+ SDWORD plm_SS_MsgState = 0, plm_SS_Severity = 0;
+ SQLBIGINT plm_Rownumber = 0;
+ USHORT plm_SS_Line;
+ SQLSMALLINT plm_cbSS_Procname, plm_cbSS_Srvname;
+ SQLWCHAR plm_SS_Procname[MAXNAME], plm_SS_Srvname[MAXNAME];
+
+ while (plm_retcode != SQL_NO_DATA_FOUND)
+ {
+ plm_retcode = SQLGetDiagRec(handle_type, handle,
+ plm_cRecNmbr, plm_szSqlState, &plm_pfNativeError,
+ plm_szErrorMsg, SQL_MAX_MESSAGE_LENGTH, &plm_pcbErrorMsg);
+
+ // Note that if the application has not yet made a
+ // successful connection, the SQLGetDiagField
+ // information has not yet been cached by ODBC
+ // Driver Manager and these calls to SQLGetDiagField
+ // will fail.
+ //
+ if (plm_retcode != SQL_NO_DATA_FOUND)
+ {
+ if (ConnInd)
+ {
+ plm_retcode = SQLGetDiagField(
+ handle_type, handle, plm_cRecNmbr,
+ SQL_DIAG_ROW_NUMBER, &plm_Rownumber,
+ SQL_IS_INTEGER,
+ NULL);
+
+ plm_retcode = SQLGetDiagField(
+ handle_type, handle, plm_cRecNmbr,
+ SQL_DIAG_SS_LINE, &plm_SS_Line,
+ SQL_IS_INTEGER,
+ NULL);
+
+ plm_retcode = SQLGetDiagField(
+ handle_type, handle, plm_cRecNmbr,
+ SQL_DIAG_SS_MSGSTATE, &plm_SS_MsgState,
+ SQL_IS_INTEGER,
+ NULL);
+
+ plm_retcode = SQLGetDiagField(
+ handle_type, handle, plm_cRecNmbr,
+ SQL_DIAG_SS_SEVERITY, &plm_SS_Severity,
+ SQL_IS_INTEGER,
+ NULL);
+
+ plm_retcode = SQLGetDiagField(
+ handle_type, handle, plm_cRecNmbr,
+ SQL_DIAG_SS_PROCNAME, &plm_SS_Procname,
+ sizeof(plm_SS_Procname),
+ &plm_cbSS_Procname);
+
+ plm_retcode = SQLGetDiagField(
+ handle_type, handle, plm_cRecNmbr,
+ SQL_DIAG_SS_SRVNAME, &plm_SS_Srvname,
+ sizeof(plm_SS_Srvname),
+ &plm_cbSS_Srvname);
+
+ printf_s("Msg %d, SevLevel %d, State %d, SQLState %ls\n",
+ plm_pfNativeError,
+ plm_SS_Severity,
+ plm_SS_MsgState,
+ plm_szSqlState);
+ }
+
+ printf_s("%ls\n", plm_szErrorMsg);
+
+ if (pBackupSuccess && plm_pfNativeError == 3014)
+ {
+ *pBackupSuccess = TRUE;
+ }
+ }
+
+ plm_cRecNmbr++; //Increment to next diagnostic record.
+ } // End while.
+}
+
+
+
+//------------------------------------------------------------------
+// The mainline of the ODBC thread.
+//
+// Returns TRUE if a successful backup/restore is performed.
+//
+unsigned __stdcall
+SQLRoutine(void* parms)
+{
+ bool doBackup = (bool)parms;
+
+ wchar_t sqlCommand[1024]; // way more space than we'll need.
+ bool successDetected = false;
+
+ // ODBC handles
+ //
+ SQLHENV henv = nullptr;
+ SQLHDBC hdbc = nullptr;
+ SQLHSTMT hstmt = nullptr;
+
+
+ swprintf_s(sqlCommand, L"%s DATABASE PUBS %s VIRTUAL_DEVICE='%ls'",
+ (doBackup) ? L"BACKUP" : L"RESTORE",
+ (doBackup) ? L"TO" : L"FROM",
+ wVdsName);
+
+ bool sentSQL = false;
+ int rc;
+
+ #define MAX_CONN_OUT 1024
+ SQLWCHAR szOutConn[MAX_CONN_OUT];
+ SQLSMALLINT cbOutConn;
+
+ // Initialize the ODBC environment.
+ //
+ if (SQLAllocHandle(SQL_HANDLE_ENV, nullptr, &henv) == SQL_ERROR)
+ goto exit;
+
+ // This is an ODBC v3 application
+ //
+ SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, SQL_IS_INTEGER);
+
+ // Allocate a connection handle
+ //
+ if (SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc) == SQL_ERROR)
+ {
+ printf("AllocHandle on DBC failed.");
+ goto exit;
+ }
+
+ // Connect to the server using Trusted connection.
+ // Trusted connection uses integrated NT security.
+ // If you want to use mixed-mode Authentication, please set Trusted_Connection to no.
+ //
+ // To use a named instance, change "SERVER=." to "SERVER=.\\instance_name"
+ //
+
+ rc = SQLDriverConnectW(
+ hdbc,
+ nullptr, // no diaglogs please
+ const_cast(L"DRIVER={SQL Server};Trusted_Connection=yes;SERVER=."),
+ SQL_NTS,
+ szOutConn,
+ MAX_CONN_OUT,
+ &cbOutConn,
+ SQL_DRIVER_NOPROMPT);
+
+ if (rc == SQL_ERROR)
+ {
+ SQLWCHAR szSqlState[20];
+ SQLINTEGER ssErr;
+ SQLWCHAR szErrorMsg[MAX_CONN_OUT];
+ SQLSMALLINT cbErrorMsg;
+
+ printf("Connect fails\n");
+
+ rc = SQLError(
+ henv, hdbc, SQL_NULL_HSTMT,
+ szSqlState,
+ &ssErr,
+ szErrorMsg,
+ MAX_CONN_OUT,
+ &cbErrorMsg);
+
+ printf("msg=%ls\n", szErrorMsg);
+
+ goto exit;
+ }
+
+ // Get a statement handle
+ //
+ if (SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt) == SQL_ERROR)
+ {
+ printf("Failed to get statement handle\n");
+
+ ProcessMessages(SQL_HANDLE_DBC, hdbc, true, nullptr);
+ goto exit;
+ }
+
+ // Execute the SQL
+ //
+ printf_s("Executing %ls\n", sqlCommand);
+
+ rc = SQLExecDirectW(hstmt, const_cast(sqlCommand), SQL_NTS);
+
+ // Extract all the resulting messages
+ //
+
+ SQLSMALLINT numResultCols;
+ while (1)
+ {
+ switch (rc)
+ {
+ case SQL_ERROR:
+ successDetected = false;
+ ProcessMessages(SQL_HANDLE_STMT, hstmt, true, &successDetected);
+ if (!successDetected)
+ {
+ printf("Errors resulted in failure of the command\n");
+ goto exit;
+ }
+ printf("Errors were encountered but the command was able to recover and successfully complete.\n");
+ break;
+
+ case SQL_SUCCESS_WITH_INFO:
+ ProcessMessages(SQL_HANDLE_STMT, hstmt, true, nullptr);
+ // fall through
+
+ case SQL_SUCCESS:
+ successDetected = true;
+
+ numResultCols = 0;
+ SQLNumResultCols(hstmt, &numResultCols);
+ if (numResultCols > 0)
+ {
+ printf("A result set with %d columns was produced\n",
+ (int)numResultCols);
+ }
+ break;
+
+ case SQL_NO_DATA:
+ // All results have been processed. We are done.
+ //
+ goto exit;
+
+ case SQL_NEED_DATA:
+ case SQL_INVALID_HANDLE:
+ case SQL_STILL_EXECUTING:
+ default:
+ successDetected = false;
+ printf("Unexpected SQLExec result %d\n", rc);
+ goto exit;
+ }
+ rc = SQLMoreResults(hstmt);
+ }
+
+exit:
+ // Release the ODBC resources.
+ //
+ if (hstmt != nullptr)
+ {
+ SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
+ hstmt = nullptr;
+ }
+
+ if (hdbc != nullptr)
+ {
+ SQLDisconnect(hdbc);
+ SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
+ hdbc = nullptr;
+ }
+
+ if (henv != nullptr)
+ {
+ SQLFreeHandle(SQL_HANDLE_ENV, henv);
+ henv = nullptr;
+ }
+
+ return successDetected;
+}
+
+
+//------------------------------------------------------------
+//
+// execSQL: Send the SQL to the server via ODBC.
+//
+// Return the thread handle (NULL on error).
+//
+HANDLE execSQL(bool doBackup)
+{
+ unsigned int threadId;
+ HANDLE hThread;
+
+ hThread = (HANDLE)_beginthreadex(
+ nullptr, 0, SQLRoutine, (void*)doBackup, 0, &threadId);
+ if (hThread == nullptr)
+ {
+ printf("Failed to create thread. errno is %d\n", errno);
+ }
+ return hThread;
+}
+
+//------------------------------------------------------------
+//
+// checkSQL: Wait for the T-SQL to complete,
+// returns TRUE if statement successfully executed.
+//
+bool checkSQL(HANDLE hThread)
+{
+ if (hThread == nullptr)
+ return false;
+
+ DWORD rc = WaitForSingleObject(hThread, INFINITE);
+ if (rc != WAIT_OBJECT_0)
+ {
+ printf("checkSQL failed: %d\n", rc);
+ return false;
+ }
+ if (!GetExitCodeThread(hThread, &rc))
+ {
+ printf("failed to get exit code: %d\n", GetLastError());
+ return false;
+ }
+ return rc == true;
+}
+
+
+
+
+//----------------------------------------------------------------------------------
+// VDI data transfer handler.
+//
+// This routine reads commands from the server until a 'Close' status is received.
+// It simply reads or writes a file 'superbak.dmp' in the current directory.
+//
+void performTransfer(
+ IClientVirtualDevice* vd,
+ int backup)
+{
+ FILE* fh;
+ char* fname = (char*)"superbak.dmp";
+ VDC_Command* cmd;
+ DWORD completionCode;
+ DWORD bytesTransferred;
+ HRESULT hr;
+
+ errno_t error = fopen_s(&fh, fname, (backup) ? "wb" : "rb");
+ if (error != 0)
+ {
+ printf("Failed to open: %s\n", fname);
+ return;
+ }
+
+ while (SUCCEEDED(hr = vd->GetCommand(INFINITE, &cmd)))
+ {
+ bytesTransferred = 0;
+ switch (cmd->commandCode)
+ {
+ case VDC_Read:
+ bytesTransferred = fread(cmd->buffer, 1, cmd->size, fh);
+ if (bytesTransferred == cmd->size)
+ completionCode = ERROR_SUCCESS;
+ else
+ // assume failure is eof
+ completionCode = ERROR_HANDLE_EOF;
+ break;
+
+ case VDC_Write:
+ bytesTransferred = fwrite(cmd->buffer, 1, cmd->size, fh);
+ if (bytesTransferred == cmd->size)
+ {
+ completionCode = ERROR_SUCCESS;
+ }
+ else
+ // assume failure is disk full
+ completionCode = ERROR_DISK_FULL;
+ break;
+
+ case VDC_Flush:
+ fflush(fh);
+ completionCode = ERROR_SUCCESS;
+ break;
+
+ case VDC_ClearError:
+ completionCode = ERROR_SUCCESS;
+ break;
+
+ default:
+ // If command is unknown...
+ completionCode = ERROR_NOT_SUPPORTED;
+ }
+
+ hr = vd->CompleteCommand(cmd, completionCode, bytesTransferred, 0);
+ if (FAILED(hr))
+ {
+ printf("Completion Failed: x%X\n", hr);
+ break;
+ }
+ }
+
+ if (hr != VD_E_CLOSE)
+ {
+ printf("Unexpected termination: x%X\n", hr);
+ }
+ else
+ {
+ // As far as the data transfer is concerned, no
+ // errors occurred. The code which issues the SQL
+ // must determine if the backup/restore was
+ // really successful.
+ //
+ printf("Successfully completed data transfer.\n");
+ }
+
+ fclose(fh);
+}
diff --git a/samples/features/sqlvdi/osimple/osimple.vcxproj b/samples/features/sqlvdi/osimple/osimple.vcxproj
new file mode 100644
index 0000000000..aae1e09a9a
--- /dev/null
+++ b/samples/features/sqlvdi/osimple/osimple.vcxproj
@@ -0,0 +1,142 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 17.0
+ Win32Proj
+ {a0d4d328-678f-46d3-8af1-3530d8c55507}
+ osimple
+ 10.0
+
+
+
+ Application
+ true
+ v143
+ Unicode
+
+
+ Application
+ false
+ v143
+ true
+ Unicode
+
+
+ Application
+ true
+ v143
+ Unicode
+ false
+
+
+ Application
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Level3
+ true
+ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+ Level3
+ true
+ _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ ..\include
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/features/sqlvdi/osimple/osimple.vcxproj.filters b/samples/features/sqlvdi/osimple/osimple.vcxproj.filters
new file mode 100644
index 0000000000..ca1fc0599c
--- /dev/null
+++ b/samples/features/sqlvdi/osimple/osimple.vcxproj.filters
@@ -0,0 +1,33 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+ Source Files
+
+
+
\ No newline at end of file
diff --git a/samples/features/sqlvdi/simple/simple.cpp b/samples/features/sqlvdi/simple/simple.cpp
new file mode 100644
index 0000000000..e52dcd0b6c
--- /dev/null
+++ b/samples/features/sqlvdi/simple/simple.cpp
@@ -0,0 +1,347 @@
+/***********************************************************************
+Copyright (c) Microsoft Corporation
+All Rights Reserved.
+***********************************************************************/
+// This source code is an intended supplement to the Microsoft SQL
+// Server online references and related electronic documentation.
+//
+// This sample is for instructional purposes only.
+// Code contained herein is not intended to be used "as is" in real applications.
+//
+// simple.cpp
+//
+// This is a sample program used to demonstrate the Virtual Device Interface
+// feature of Microsoft SQL Server.
+//
+// The program will backup or restore the 'pubs' sample database.
+//
+// The program accepts a single command line parameter.
+// One of:
+// b perform a backup
+// r perform a restore
+//
+
+
+// To gain access to free threaded COM, you'll need to define _WIN32_DCOM
+// before the system headers, either in the source (as in this example),
+// or when invoking the compiler (by using /D "_WIN32_DCOM")
+//
+#define _WIN32_DCOM
+
+#include // for 'CoInitialize()'
+#include // for file operations
+#include // for toupper ()
+#include // for spawn ()
+
+#include "vdi.h" // interface declaration
+#include "vdierror.h" // error constants
+
+#include "vdiguid.h" // define the interface identifiers.
+ // IMPORTANT: vdiguid.h can only be included in one source file.
+ //
+
+void performTransfer(
+ IClientVirtualDevice* vd,
+ bool backup);
+
+int execSQL(bool doBackup);
+
+// Using a GUID for the VDS Name is a good way to assure uniqueness.
+//
+WCHAR wVdsName[50];
+
+
+//
+// main function
+//
+int main(int argc, char* argv[])
+{
+ HRESULT hr;
+ IClientVirtualDeviceSet2* vds = nullptr;
+ IClientVirtualDevice* vd = nullptr;
+
+ VDConfig config;
+ bool badParm = true;
+ bool doBackup;
+ bool isUnnamed = false;
+ int hProcess;
+ int termCode;
+
+ // Check the input parm
+ //
+ if (argc == 2)
+ {
+ char param = toupper(argv[1][0]);
+
+ if (param == 'B')
+ {
+ doBackup = true;
+ badParm = false;
+ }
+ else if (param == 'R')
+ {
+ doBackup = false;
+ badParm = false;
+ }
+ }
+
+ if (badParm)
+ {
+ printf("usage: simple {B|R}\n"
+ "Demonstrate a Backup or Restore using the Virtual Device Interface\n");
+ exit(1);
+ }
+
+ // Initialize COM Library
+ // Note: _WIN32_DCOM must be defined during the compile.
+ //
+ hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
+
+ if (FAILED(hr))
+ {
+ printf("Coinit fails: x%X\n", hr);
+ exit(1);
+ }
+
+ // Get an interface to the device set.
+ hr = CoCreateInstance(
+ CLSID_MSSQL_ClientVirtualDeviceSet,
+ nullptr,
+ CLSCTX_INPROC_SERVER,
+ IID_IClientVirtualDeviceSet2,
+ (void**)&vds);
+
+ if (FAILED(hr))
+ {
+ // This failure might happen if the DLL was not registered,
+ // or if the application is using the wrong interface id (IID).
+ //
+ printf("Could not create component: x%X\n", hr);
+ printf("Check registration of SQLVDI.DLL and value of IID\n");
+ goto exit;
+ }
+
+ // Setup the VDI configuration we want to use.
+ // This program doesn't use any fancy features, so the
+ // only field to setup is the deviceCount.
+ //
+ // The server will treat the virtual device just like a pipe:
+ // I/O will be strictly sequential with only the basic commands.
+ //
+ memset(&config, 0, sizeof(config));
+ config.deviceCount = 1;
+
+ // Create a GUID to use for a unique virtual device name
+ //
+ GUID vdsId;
+ CoCreateGuid(&vdsId);
+ StringFromGUID2(vdsId, wVdsName, 49);
+
+ // Create the virtual device set
+ // for use by the default instance.
+ //
+ // To use a named instance, change the
+ // first parameter in CreateEx to your instance's name.
+ //
+ hr = vds->CreateEx(nullptr, wVdsName, &config);
+ if (FAILED(hr))
+ {
+ printf("VDS::Create fails: x%X", hr);
+ goto exit;
+ }
+
+ // Send the SQL command, by starting 'isql' in a subprocess.
+ //
+ printf("\nSending the SQL...\n");
+
+ hProcess = execSQL(doBackup);
+ if (hProcess == -1)
+ {
+ printf("execSQL failed.\n");
+ goto shutdown;
+ }
+
+ // Wait for the server to connect, completing the configuration.
+ //
+ hr = vds->GetConfiguration(10000, &config);
+ if (FAILED(hr))
+ {
+ printf("VDS::Getconfig fails: x%X\n", hr);
+ if (hr == VD_E_TIMEOUT)
+ {
+ printf("Timed out. Was Microsoft SQLServer running?\n");
+ }
+ goto shutdown;
+ }
+
+ // Open the single device in the set.
+ //
+ hr = vds->OpenDevice(wVdsName, &vd);
+ if (FAILED(hr))
+ {
+ printf("VDS::OpenDevice fails: x%X\n", hr);
+ goto shutdown;
+ }
+
+ printf("\nPerforming data transfer...\n");
+
+ performTransfer(vd, doBackup);
+
+
+shutdown:
+
+ // Close the set
+ //
+ vds->Close();
+
+ // Obtain the SQL completion information, by waiting for isql to exit.
+ //
+ if (hProcess == _cwait(&termCode, hProcess, 0))
+ {
+ if (termCode == 0)
+ printf("\nThe SQL command executed successfully.\n");
+ else
+ printf("\nThe SQL command failed.\n");
+ }
+ else
+ {
+ printf("cwait failed: %d\n", errno);
+ }
+
+ // COM reference counting: Release the interface.
+ //
+ // Rather than releasing it, the application has the
+ // option to 'Create' another set, reusing the interface
+ // that it currently has. Of course that applies to
+ // a real application, not this simple sample!
+ //
+ vds->Release();
+
+exit:
+
+ // Uninitialize COM Library
+ //
+ CoUninitialize();
+
+ return 0;
+}
+
+//
+// Execute a basic backup/restore, by spawning a process to execute 'isql'.
+//
+// Returns:
+// -1 : failed to spawn
+// else : a "process handle"
+//
+int execSQL(bool doBackup)
+{
+ wchar_t sqlCommand[1024]; // plenty of space for our purpose
+
+ // To use a named instance, change "-S ." to "-S .\\instance_name"
+ swprintf_s(sqlCommand, L"-S . -Q\"%s DATABASE PUBS %s VIRTUAL_DEVICE='%ls'\"",
+ (doBackup) ? L"BACKUP" : L"RESTORE",
+ (doBackup) ? L"TO" : L"FROM",
+ wVdsName);
+ intptr_t rc;
+
+ wprintf(L"spawning osql to execute: %ls\n", sqlCommand);
+
+ // Spawn off the osql utility to execute the SQL.
+ // Notice the '-b' which causes an error to set a non-zero
+ // exit code on error.
+ //
+ rc = _wspawnlp(_P_NOWAIT, L"osql", L"osql", L"-E", L"-b",
+ sqlCommand, NULL);
+
+ if (rc == -1)
+ {
+ printf("Spawn failed with error: %d\n", errno);
+ }
+
+ return (rc);
+}
+
+// This routine reads commands from the server until a 'Close' status is received.
+// It simply reads or writes a file 'superbak.dmp' in the current directory.
+//
+void performTransfer(
+ IClientVirtualDevice* vd,
+ bool backup)
+{
+ FILE* fh;
+ char* fname = (char*)"superbak.dmp";
+ VDC_Command* cmd;
+ DWORD completionCode;
+ DWORD bytesTransferred;
+ HRESULT hr;
+
+ errno_t error = fopen_s(&fh, fname, (backup) ? "wb" : "rb");
+ if (error != 0)
+ {
+ printf("Failed to open: %s\n", fname);
+ return;
+ }
+
+ while (SUCCEEDED(hr = vd->GetCommand(INFINITE, &cmd)))
+ {
+ bytesTransferred = 0;
+ switch (cmd->commandCode)
+ {
+ case VDC_Read:
+ bytesTransferred = fread(cmd->buffer, 1, cmd->size, fh);
+ if (bytesTransferred == cmd->size)
+ completionCode = ERROR_SUCCESS;
+ else
+ // assume failure is eof
+ completionCode = ERROR_HANDLE_EOF;
+ break;
+
+ case VDC_Write:
+ bytesTransferred = fwrite(cmd->buffer, 1, cmd->size, fh);
+ if (bytesTransferred == cmd->size)
+ {
+ completionCode = ERROR_SUCCESS;
+ }
+ else
+ // assume failure is disk full
+ completionCode = ERROR_DISK_FULL;
+ break;
+
+ case VDC_Flush:
+ fflush(fh);
+ completionCode = ERROR_SUCCESS;
+ break;
+
+ case VDC_ClearError:
+ completionCode = ERROR_SUCCESS;
+ break;
+
+ default:
+ // If command is unknown...
+ completionCode = ERROR_NOT_SUPPORTED;
+ }
+
+ hr = vd->CompleteCommand(cmd, completionCode, bytesTransferred, 0);
+ if (FAILED(hr))
+ {
+ printf("Completion Failed: x%X\n", hr);
+ break;
+ }
+ }
+
+ if (hr != VD_E_CLOSE)
+ {
+ printf("Unexpected termination: x%X\n", hr);
+ }
+ else
+ {
+ // As far as the data transfer is concerned, no
+ // errors occurred. The code which issues the SQL
+ // must determine if the backup/restore was
+ // really successful.
+ //
+ printf("Successfully completed data transfer.\n");
+ }
+
+ fclose(fh);
+}
\ No newline at end of file
diff --git a/samples/features/sqlvdi/simple/simple.vcxproj b/samples/features/sqlvdi/simple/simple.vcxproj
new file mode 100644
index 0000000000..65fd7f65ce
--- /dev/null
+++ b/samples/features/sqlvdi/simple/simple.vcxproj
@@ -0,0 +1,141 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 17.0
+ Win32Proj
+ {13df2fa4-99d2-44ac-b218-3463730b6f08}
+ simple
+ 10.0
+
+
+
+ Application
+ true
+ v143
+ Unicode
+
+
+ Application
+ false
+ v143
+ true
+ Unicode
+
+
+ Application
+ true
+ v143
+ Unicode
+
+
+ Application
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Level3
+ true
+ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+ Level3
+ true
+ _DEBUG;_CONSOLE;%(PreprocessorDefinitions);
+ true
+ ..\include
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/features/sqlvdi/simple/simple.vcxproj.filters b/samples/features/sqlvdi/simple/simple.vcxproj.filters
new file mode 100644
index 0000000000..3a45a4311d
--- /dev/null
+++ b/samples/features/sqlvdi/simple/simple.vcxproj.filters
@@ -0,0 +1,33 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+ Source Files
+
+
+
\ No newline at end of file
diff --git a/samples/features/sqlvdi/snapshot/snapshot.cpp b/samples/features/sqlvdi/snapshot/snapshot.cpp
new file mode 100644
index 0000000000..2e0cc9f199
--- /dev/null
+++ b/samples/features/sqlvdi/snapshot/snapshot.cpp
@@ -0,0 +1,828 @@
+/***********************************************************************
+Copyright (c) Microsoft Corporation
+All Rights Reserved.
+***********************************************************************/
+// This source code is an intended supplement to the Microsoft SQL
+// Server online references and related electronic documentation.
+//
+// This sample is for instructional purposes only.
+// Code contained herein is not intended to be used "as is" in real applications.
+//
+// snapshot.cpp
+//
+// This sample extends the "osimple.cpp" sample to demonstrate BACKUP WITH SNAPSHOT.
+// It is not fully functional. The ability to take/mount snapshots must be provided.
+//
+// This is a sample program used to demonstrate the Virtual Device Interface
+// feature of Microsoft SQL Server together with an ODBC connection.
+//
+// The program will request backup or restore of a single database
+// on some instance of sql server.
+//
+// The program accepts input:
+// {b | r} []
+// b -> backup
+// r -> restore
+//
+
+
+// To gain access to free threaded COM, you'll need to define _WIN32_DCOM
+// before the system headers, either in the source (as in this example),
+// or when invoking the compiler (by using /D "_WIN32_DCOM")
+//
+// This sample uses Windows NT Authentication, other than mixed mode security,
+// to establish connections. If you want to use mixed mode security, please
+// set Trusted_Connection to no in the SQLDriverConnect command.
+// Make necessary change to the server name in the SQLDriverConnect command.
+//
+#define _WIN32_DCOM
+
+#include // for 'CoInitialize()'
+#include // for file operations
+#include // for toupper ()
+#include // for C library-safe _beginthreadex,_endthreadex
+
+#include "vdi.h" // interface declaration
+#include "vdierror.h" // error constants
+
+#include "vdiguid.h" // define the interface identifiers.
+ // IMPORTANT: vdiguid.h can only be included in one source file.
+ //
+
+#include
+#include "sql.h"
+#include "sqlext.h"
+#include "odbcss.h"
+
+void performTransfer(
+ IClientVirtualDeviceSet2* vds,
+ IClientVirtualDevice* vd,
+ int backup);
+
+HANDLE execSQL(bool doBackup, WCHAR* pInstanceName, WCHAR* pDbName, WCHAR* pVdsName);
+bool checkSQL(HANDLE);
+
+bool ynPrompt(const char* str);
+
+//------------------------------------------------------------
+//
+// Mainline
+//
+int __cdecl
+main(int argc, char* argv[])
+{
+ HRESULT hr;
+ IClientVirtualDeviceSet2* vds = nullptr;
+ IClientVirtualDevice* vd = nullptr;
+
+ VDConfig config;
+ bool badParm = true;
+ bool doBackup;
+ HANDLE hThread = nullptr;
+ char* pDbName = nullptr;
+ char* pInstanceName = nullptr;
+ WCHAR wInstanceName[128] = { 0 };
+ int rc = 0;
+
+ // Check the input parm
+ //
+ if (argc >= 3)
+ {
+ char param = toupper(argv[1][0]);
+
+ if (param == 'B')
+ {
+ doBackup = true;
+ badParm = false;
+ }
+ else if (param == 'R')
+ {
+ doBackup = false;
+ badParm = false;
+ }
+
+ pDbName = argv[2];
+
+ if (argc == 4)
+ {
+ pInstanceName = argv[3];
+ }
+ }
+
+ if (badParm)
+ {
+ printf("usage: snapshot {B|R} []\n"
+ "Demonstrate a Backup or Restore WITH SNAPSHOT\n");
+ printf("\n\n** NOTE **\n The ability to take or mount snapshots must be implemented\n"
+ "before this sample is truely functional.\n");
+ exit(1);
+ }
+
+ printf("Performing a %s of %s on %s using a VIRTUAL_DEVICE.\n",
+ (doBackup) ? "BACKUP" : "RESTORE",
+ pDbName,
+ (pInstanceName) ? pInstanceName : "Default");
+
+ // Initialize COM Library
+ // Note: _WIN32_DCOM must be defined during the compile.
+ //
+ hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
+
+ if (FAILED(hr))
+ {
+ printf("Coinit fails: x%X\n", hr);
+ exit(1);
+ }
+
+ // Get an interface to the device set.
+ // Notice how we use a single IID for both the class and interface
+ // identifiers.
+ //
+ hr = CoCreateInstance(
+ CLSID_MSSQL_ClientVirtualDeviceSet,
+ nullptr,
+ CLSCTX_INPROC_SERVER,
+ IID_IClientVirtualDeviceSet2,
+ (void**)&vds);
+
+ if (FAILED(hr))
+ {
+ // This failure might happen if the DLL was not registered,
+ // or if the application is using the wrong interface id (IID).
+ //
+ printf("Could not create component: x%X\n", hr);
+ printf("Check registration of SQLVDI.DLL and value of IID\n");
+ goto exit;
+ }
+
+ // Setup the VDI configuration we want to use.
+ // This program doesn't use any fancy features, so the
+ // only field to setup is the deviceCount.
+ //
+ // The server will treat the virtual device just like a pipe:
+ // I/O will be strictly sequential with only the basic commands.
+ //
+ memset(&config, 0, sizeof(config));
+ config.deviceCount = 1;
+ config.features = VDF_SnapshotPrepare;
+
+ // Create a GUID to use for a unique virtual device name
+ //
+ GUID vdsId;
+ WCHAR wVdsName[50];
+ CoCreateGuid(&vdsId);
+ StringFromGUID2(vdsId, wVdsName, 49);
+
+ // Create the virtual device set
+ // Notice that we only support unicode interfaces
+ //
+
+ if (pInstanceName)
+ {
+ rc = MultiByteToWideChar(CP_ACP, 0,
+ pInstanceName, strlen(pInstanceName),
+ wInstanceName, 127);
+ }
+ wInstanceName[rc] = 0;
+
+ hr = vds->CreateEx(wInstanceName, wVdsName, &config);
+ if (FAILED(hr))
+ {
+ printf("VDS::Create fails: x%X", hr);
+ goto exit;
+ }
+
+ // Send the SQL command, by starting a thread to handle the ODBC
+ //
+ printf("\nSending the SQL...\n");
+
+ WCHAR wDbName[128];
+ MultiByteToWideChar(CP_ACP, 0,
+ pDbName, -1,
+ wDbName, 127);
+
+ hThread = execSQL(doBackup, wInstanceName, wDbName, wVdsName);
+ if (hThread == nullptr)
+ {
+ printf("execSQL failed.\n");
+ goto shutdown;
+ }
+
+ // Wait for the server to connect, completing the configuration.
+ //
+ printf("\nWaiting for SQLServer to respond...\n");
+
+ while (FAILED(hr = vds->GetConfiguration(1000, &config)))
+ {
+ if (hr == VD_E_TIMEOUT)
+ {
+ // Check on the SQL thread
+ //
+ DWORD rc = WaitForSingleObject(hThread, 1000);
+ if (rc == WAIT_OBJECT_0)
+ {
+ printf("SQL command failed before VD transfer\n");
+ goto shutdown;
+ }
+ if (rc == WAIT_TIMEOUT)
+ {
+ continue;
+ }
+ printf("Check on SQL failed: %d\n", rc);
+ goto shutdown;
+ }
+
+ printf("VDS::Getconfig fails: x%X\n", hr);
+ goto shutdown;
+ }
+
+ // Open the single device in the set.
+ //
+ hr = vds->OpenDevice(wVdsName, &vd);
+ if (FAILED(hr))
+ {
+ printf("VDS::OpenDevice fails: x%X\n", hr);
+ goto shutdown;
+ }
+
+ printf("\nPerforming data transfer...\n");
+
+ performTransfer(vds, vd, doBackup);
+
+
+shutdown:
+
+ // Close the set
+ //
+ vds->Close();
+
+ // Obtain the SQL completion information
+ //
+ if (hThread != nullptr)
+ {
+ if (checkSQL(hThread))
+ printf("\nThe SQL command executed successfully.\n");
+ else
+ printf("\nThe SQL command failed.\n");
+
+ CloseHandle(hThread);
+ }
+
+ // COM reference counting: Release the interface.
+ //
+ vds->Release();
+
+exit:
+ // Uninitialize COM Library
+ //
+ CoUninitialize();
+
+ return 0;
+}
+
+//---------------------------------------------------------------------------
+// ODBC Message processing routine.
+// This is SQLServer specific, to show native message IDs, sev, state.
+//
+// The routine will watch for message 3014 in order to detect
+// a successful backup/restore operation. This is useful for
+// operations like RESTORE which can sometimes recover from
+// errors (error messages will be followed by the 3014 success message).
+//
+void ProcessMessages(
+ SQLSMALLINT handle_type, // ODBC handle type
+ SQLHANDLE handle, // ODBC handle
+ bool ConnInd, // TRUE if sucessful connection made
+ bool* pBackupSuccess) // Set TRUE if a 3014 message is seen.
+{
+ RETCODE plm_retcode = SQL_SUCCESS;
+ SQLWCHAR plm_szSqlState[SQL_SQLSTATE_SIZE + 1];
+ SQLWCHAR plm_szErrorMsg[SQL_MAX_MESSAGE_LENGTH + 1];
+ SDWORD plm_pfNativeError = 0L;
+ SWORD plm_pcbErrorMsg = 0;
+ SQLSMALLINT plm_cRecNmbr = 1;
+ SDWORD plm_SS_MsgState = 0, plm_SS_Severity = 0;
+ SQLBIGINT plm_Rownumber = 0;
+ USHORT plm_SS_Line;
+ SQLSMALLINT plm_cbSS_Procname, plm_cbSS_Srvname;
+ SQLCHAR plm_SS_Procname[MAXNAME], plm_SS_Srvname[MAXNAME];
+
+ while (plm_retcode != SQL_NO_DATA_FOUND)
+ {
+ plm_retcode = SQLGetDiagRec(handle_type, handle,
+ plm_cRecNmbr, plm_szSqlState, &plm_pfNativeError,
+ plm_szErrorMsg, SQL_MAX_MESSAGE_LENGTH, &plm_pcbErrorMsg);
+
+ // Note that if the application has not yet made a
+ // successful connection, the SQLGetDiagField
+ // information has not yet been cached by ODBC
+ // Driver Manager and these calls to SQLGetDiagField
+ // will fail.
+ //
+ if (plm_retcode != SQL_NO_DATA_FOUND)
+ {
+ if (ConnInd)
+ {
+ plm_retcode = SQLGetDiagField(
+ handle_type, handle, plm_cRecNmbr,
+ SQL_DIAG_ROW_NUMBER, &plm_Rownumber,
+ SQL_IS_INTEGER,
+ NULL);
+
+ plm_retcode = SQLGetDiagField(
+ handle_type, handle, plm_cRecNmbr,
+ SQL_DIAG_SS_LINE, &plm_SS_Line,
+ SQL_IS_INTEGER,
+ NULL);
+
+ plm_retcode = SQLGetDiagField(
+ handle_type, handle, plm_cRecNmbr,
+ SQL_DIAG_SS_MSGSTATE, &plm_SS_MsgState,
+ SQL_IS_INTEGER,
+ NULL);
+
+ plm_retcode = SQLGetDiagField(
+ handle_type, handle, plm_cRecNmbr,
+ SQL_DIAG_SS_SEVERITY, &plm_SS_Severity,
+ SQL_IS_INTEGER,
+ NULL);
+
+ plm_retcode = SQLGetDiagField(
+ handle_type, handle, plm_cRecNmbr,
+ SQL_DIAG_SS_PROCNAME, &plm_SS_Procname,
+ sizeof(plm_SS_Procname),
+ &plm_cbSS_Procname);
+
+ plm_retcode = SQLGetDiagField(
+ handle_type, handle, plm_cRecNmbr,
+ SQL_DIAG_SS_SRVNAME, &plm_SS_Srvname,
+ sizeof(plm_SS_Srvname),
+ &plm_cbSS_Srvname);
+
+ printf_s("Msg %ld, SevLevel %ld, State %ld, SQLState %ls\n",
+ plm_pfNativeError,
+ plm_SS_Severity,
+ plm_SS_MsgState,
+ plm_szSqlState);
+ }
+
+ printf("%ls\n", plm_szErrorMsg);
+
+ if (pBackupSuccess && plm_pfNativeError == 3014)
+ {
+ *pBackupSuccess = TRUE;
+ }
+ }
+
+ plm_cRecNmbr++; //Increment to next diagnostic record.
+ } // End while.
+}
+
+
+
+//------------------------------------------------------------------
+// The mainline of the ODBC thread.
+//
+// Returns TRUE if a successful backup/restore is performed.
+//
+struct PARMS
+{
+ bool doBackup;
+ WCHAR* pInstanceName;
+ WCHAR* pDbName;
+ WCHAR* pVdsName;
+};
+
+unsigned __stdcall
+SQLRoutine(void* input)
+{
+ PARMS* parms = (PARMS*)input;
+
+ SQLWCHAR* pSQLText; // the command being executed.
+ bool successDetected = false;
+
+ // ODBC handles
+ //
+ SQLHENV henv = nullptr;
+ SQLHDBC hdbc = nullptr;
+ SQLHSTMT hstmt = nullptr;
+
+ WCHAR sqlCommand[1024];
+ SQLWCHAR connectString[200] = { 0 };
+
+ bool sentSQL = false;
+ int rc;
+
+ #define MAX_CONN_OUT 1024
+ SQLWCHAR szOutConn[MAX_CONN_OUT] = { 0 };
+ SQLSMALLINT cbOutConn;
+
+
+ // Generate the command to execute
+ //
+ swprintf_s(sqlCommand, L"%ls DATABASE [%ls] %ls VIRTUAL_DEVICE='%ls' WITH SNAPSHOT",
+ parms->doBackup ? L"BACKUP" : L"RESTORE",
+ parms->pDbName,
+ parms->doBackup ? L"TO" : L"FROM",
+ parms->pVdsName);
+
+ // Initialize the ODBC environment.
+ //
+ if (SQLAllocHandle(SQL_HANDLE_ENV, NULL, &henv) == SQL_ERROR)
+ goto exit;
+
+ // This is an ODBC v3 application
+ //
+ SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, SQL_IS_INTEGER);
+
+ // Allocate a connection handle
+ //
+ if (SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc) == SQL_ERROR)
+ {
+ printf("AllocHandle on DBC failed.");
+ goto exit;
+ }
+
+ wcscpy_s(connectString, 200, L"DRIVER={SQL Server};Trusted_Connection=yes;SERVER=.");
+ if (parms->pInstanceName)
+ {
+ swprintf_s(connectString + wcslen(connectString), 200 - wcslen(connectString), L"\\%ls", parms->pInstanceName);
+ }
+
+ printf("\n\nConnecting with: %ls\n", connectString);
+
+ // Connect to the server using Trusted connection.
+ // Trusted connection uses integrated NT security.
+ // If you want to use mixed-mode Authentication, please set Trusted_Connection to no.
+ rc = SQLDriverConnect(
+ hdbc,
+ nullptr, // no diaglogs please
+ connectString,
+ SQL_NTS,
+ szOutConn,
+ MAX_CONN_OUT,
+ &cbOutConn,
+ SQL_DRIVER_NOPROMPT);
+
+ if (rc == SQL_ERROR)
+ {
+ SQLWCHAR szSqlState[20];
+ SQLINTEGER ssErr;
+ SQLWCHAR szErrorMsg[MAX_CONN_OUT];
+ SQLSMALLINT cbErrorMsg;
+
+ printf("Connect fails\n");
+
+ rc = SQLError(
+ henv, hdbc, SQL_NULL_HSTMT,
+ szSqlState,
+ &ssErr,
+ szErrorMsg,
+ MAX_CONN_OUT,
+ &cbErrorMsg);
+
+ printf("msg=%ls\n", szErrorMsg);
+
+ goto exit;
+ }
+
+ // Get a statement handle
+ //
+ if (SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt) == SQL_ERROR)
+ {
+ printf("Failed to get statement handle\n");
+
+ ProcessMessages(SQL_HANDLE_DBC, hdbc, true, nullptr);
+ goto exit;
+ }
+
+ // Execute the SQL
+ //
+ printf("\n\nExecuting SQL: %ls\n", sqlCommand);
+
+ pSQLText = sqlCommand;
+
+ rc = SQLExecDirect(hstmt, pSQLText, SQL_NTS);
+
+ // Extract all the resulting messages
+ //
+
+ SQLSMALLINT numResultCols;
+ while (1)
+ {
+ switch (rc)
+ {
+ case SQL_ERROR:
+ successDetected = false;
+ ProcessMessages(SQL_HANDLE_STMT, hstmt, true, &successDetected);
+ if (!successDetected)
+ {
+ printf("Errors resulted in failure of the command\n");
+ goto exit;
+ }
+ printf("Errors were encountered but the command was able to recover and successfully complete.\n");
+ break;
+
+ case SQL_SUCCESS_WITH_INFO:
+ ProcessMessages(SQL_HANDLE_STMT, hstmt, TRUE, NULL);
+ // fall through
+
+ case SQL_SUCCESS:
+ successDetected = true;
+
+ numResultCols = 0;
+ SQLNumResultCols(hstmt, &numResultCols);
+ if (numResultCols > 0)
+ {
+ printf("A result set with %d columns was produced\n",
+ (int)numResultCols);
+ }
+ break;
+
+ case SQL_NO_DATA:
+ // All results have been processed. We are done.
+ //
+ goto exit;
+
+ case SQL_NEED_DATA:
+ case SQL_INVALID_HANDLE:
+ case SQL_STILL_EXECUTING:
+ default:
+ successDetected = false;
+ printf("Unexpected SQLExec result %d\n", rc);
+ goto exit;
+ }
+ rc = SQLMoreResults(hstmt);
+ }
+
+exit:
+ // Release the ODBC resources.
+ //
+ if (hstmt != nullptr)
+ {
+ SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
+ hstmt = nullptr;
+ }
+
+ if (hdbc != nullptr)
+ {
+ SQLDisconnect(hdbc);
+ SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
+ hdbc = nullptr;
+ }
+
+ if (henv != nullptr)
+ {
+ SQLFreeHandle(SQL_HANDLE_ENV, henv);
+ henv = nullptr;
+ }
+
+ return successDetected;
+}
+
+
+//------------------------------------------------------------
+//
+// execSQL: Send the SQL to the server via ODBC.
+//
+// Return the thread handle (NULL on error).
+//
+HANDLE execSQL(bool doBackup, WCHAR* pInstanceName, WCHAR* pDbName, WCHAR* pVDName)
+{
+ unsigned int threadId;
+ HANDLE hThread;
+ static PARMS parms; // yucky, but only a single command is used....
+
+ parms.doBackup = doBackup;
+ parms.pDbName = pDbName;
+ parms.pInstanceName = pInstanceName;
+ parms.pVdsName = pVDName;
+
+ hThread = (HANDLE)_beginthreadex(
+ nullptr, 0, SQLRoutine, (void*)&parms, 0, &threadId);
+ if (hThread == nullptr)
+ {
+ printf("Failed to create thread. errno is %d\n", errno);
+ }
+ return hThread;
+}
+
+//------------------------------------------------------------
+//
+// checkSQL: Wait for the T-SQL to complete,
+// returns TRUE if statement successfully executed.
+//
+bool checkSQL(HANDLE hThread)
+{
+ if (hThread == nullptr)
+ return false;
+
+ DWORD rc = WaitForSingleObject(hThread, INFINITE);
+ if (rc != WAIT_OBJECT_0)
+ {
+ printf("checkSQL failed: %d\n", rc);
+ return false;
+ }
+ if (!GetExitCodeThread(hThread, &rc))
+ {
+ printf("failed to get exit code: %d\n", GetLastError());
+ return false;
+ }
+ return rc == true;
+}
+
+
+//----------------------------------------------------------------------------------
+// Ask the user a "yes/no" question.
+// Return TRUE if he answers "yes"
+//
+bool ynPrompt(const char* str)
+{
+ char line[256];
+
+ printf("\n\n%s\n", str);
+ fgets(line, sizeof(line), stdin);
+
+ // Remove newline character if fgets reads in a newline.
+ size_t len = strlen(line);
+ if (len > 0 && line[len - 1] == '\n') {
+ line[len - 1] = '\0';
+ }
+
+ return (line[0] == 'y' || line[0] == 'Y');
+}
+
+//----------------------------------------------------------------------------------
+// VDI data transfer handler.
+//
+// This routine reads commands from the server until a 'Close' status is received.
+// It simply reads or writes a file 'superbak.dmp' in the current directory.
+//
+void performTransfer(
+ IClientVirtualDeviceSet2* iVds,
+ IClientVirtualDevice* vd,
+ int backup)
+{
+ FILE* fh;
+ char* fname = (char*)"snapshot.dmp";
+ VDC_Command* cmd;
+ DWORD completionCode;
+ DWORD bytesTransferred;
+ HRESULT hr;
+
+ errno_t error = fopen_s(&fh, fname, (backup) ? "wb" : "rb");
+ if (error != 0)
+ {
+ printf("Failed to open: %s\n", fname);
+ return;
+ }
+
+ while (SUCCEEDED(hr = vd->GetCommand(INFINITE, &cmd)))
+ {
+ bytesTransferred = 0;
+ switch (cmd->commandCode)
+ {
+ case VDC_Read:
+ bytesTransferred = fread(cmd->buffer, 1, cmd->size, fh);
+ if (bytesTransferred == cmd->size)
+ completionCode = ERROR_SUCCESS;
+ else
+ // assume failure is eof
+ completionCode = ERROR_HANDLE_EOF;
+ break;
+
+ case VDC_Write:
+ bytesTransferred = fwrite(cmd->buffer, 1, cmd->size, fh);
+ if (bytesTransferred == cmd->size)
+ {
+ completionCode = ERROR_SUCCESS;
+ }
+ else
+ // assume failure is disk full
+ completionCode = ERROR_DISK_FULL;
+ break;
+
+ case VDC_Flush:
+ fflush(fh);
+ completionCode = ERROR_SUCCESS;
+ break;
+
+ case VDC_ClearError:
+ completionCode = ERROR_SUCCESS;
+ break;
+
+ case VDC_PrepareToFreeze:
+ printf("\n*** SQL Server is prepared to freeze the database now ***\n");
+ printf("At this point the application can perform any final coordination activities.\n");
+
+ while (!ynPrompt("Are you ready to freeze the database?"))
+ {
+ if (ynPrompt("Do you want to abort?"))
+ {
+ iVds->SignalAbort();
+ return;
+ }
+ }
+ // Acknowledging this command results in a freeze of database writes.
+ // The server will then issue VDC_Write commands to record metadata
+ // about the frozen state.
+ // Then the VDC_Snapshot is issued.
+ //
+ completionCode = ERROR_SUCCESS;
+ break;
+
+ case VDC_Snapshot:
+ // At this point the metadata is complete, so the
+ // output stream can be closed. Thus it is possible
+ // to include the metadata with the data of the snapshot.
+ //
+ fclose(fh);
+ fh = nullptr;
+
+ printf("\n*** Make the snapshot now ***\n");
+
+ while (!ynPrompt("Did you complete the snapshot?"))
+ {
+ if (ynPrompt("Do you want to abort?"))
+ {
+ iVds->SignalAbort();
+ return;
+ }
+ }
+
+ // For clarity, we "unroll" the loop logic
+ // in the following block of code so you can
+ // easily see the sequence of operations.
+ //
+
+ // Tell SQLServer that the snapshot is done.
+ //
+ completionCode = ERROR_SUCCESS;
+ hr = vd->CompleteCommand(cmd, completionCode, bytesTransferred, 0);
+ if (FAILED(hr))
+ {
+ printf("Completion Failed: x%X\n", hr);
+ return;
+ }
+
+ // The only valid command will be a "close" request.
+ //
+ hr = vd->GetCommand(INFINITE, &cmd);
+ if (hr != VD_E_CLOSE)
+ {
+ printf("Unexpected snapshot termination: x%X\n", hr);
+ }
+ else
+ {
+ printf("SQLServer is aware that the snapshot is successful.\n");
+ }
+ return;
+
+ case VDC_MountSnapshot:
+ printf("\n*** Mount the snapshot now ***\n");
+
+ while (!ynPrompt("Did you complete the snapshot?"))
+ {
+ if (ynPrompt("Do you want to abort?"))
+ {
+ iVds->SignalAbort();
+ return;
+ }
+ }
+ completionCode = ERROR_SUCCESS;
+ break;
+
+ default:
+ // If command is unknown...
+ completionCode = ERROR_NOT_SUPPORTED;
+ }
+
+ hr = vd->CompleteCommand(cmd, completionCode, bytesTransferred, 0);
+ if (FAILED(hr))
+ {
+ printf("Completion Failed: x%X\n", hr);
+ break;
+ }
+ }
+
+ if (hr != VD_E_CLOSE)
+ {
+ printf("Unexpected termination: x%X\n", hr);
+ }
+ else
+ {
+ // As far as the data transfer is concerned, no
+ // errors occurred. The code which issues the SQL
+ // must determine if the backup/restore was
+ // really successful.
+ //
+ printf("Successfully completed data transfer.\n");
+ }
+
+ if (fh != nullptr)
+ {
+ fclose(fh);
+ }
+}
+
+
diff --git a/samples/features/sqlvdi/snapshot/snapshot.vcxproj b/samples/features/sqlvdi/snapshot/snapshot.vcxproj
new file mode 100644
index 0000000000..8865b53804
--- /dev/null
+++ b/samples/features/sqlvdi/snapshot/snapshot.vcxproj
@@ -0,0 +1,142 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 17.0
+ Win32Proj
+ {f51dacf4-9aba-4f84-a508-041d3c1f2399}
+ snapshot
+ 10.0
+
+
+
+ Application
+ true
+ v143
+ Unicode
+
+
+ Application
+ false
+ v143
+ true
+ Unicode
+
+
+ Application
+ true
+ v143
+ Unicode
+ false
+
+
+ Application
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Level3
+ true
+ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+ Level3
+ true
+ _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ ..\include
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/features/sqlvdi/snapshot/snapshot.vcxproj.filters b/samples/features/sqlvdi/snapshot/snapshot.vcxproj.filters
new file mode 100644
index 0000000000..1f3779567d
--- /dev/null
+++ b/samples/features/sqlvdi/snapshot/snapshot.vcxproj.filters
@@ -0,0 +1,33 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Source Files
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
\ No newline at end of file
diff --git a/samples/manage/README.md b/samples/manage/README.md
index 0a24ac8f4d..477015372a 100644
--- a/samples/manage/README.md
+++ b/samples/manage/README.md
@@ -17,3 +17,6 @@ This Solution Quick Start provides a solution for a Software-as-a-Solution (SaaS
## Windows Containers
This includes samples for setting up mssql-server in Windows Containers. Currently it only includes a link to the separately maintained [mssql-docker](https://github.com/Microsoft/mssql-docker/blob/master/windows/README.md) instructions.
+
+## Handling UPDATE STATISTICS on SQL Server PolyBase external tables
+[This sample](https://github.com/microsoft/sql-server-samples/tree/master/samples/manage/polybase/external-table/README.md) describes an option to update statistics on SQL Server PolyBase external tables.
diff --git a/samples/manage/azure-arc-enabled-sql-server/activate-pcore-license/README.md b/samples/manage/azure-arc-enabled-sql-server/activate-pcore-license/README.md
new file mode 100644
index 0000000000..2ce4b9ac7c
--- /dev/null
+++ b/samples/manage/azure-arc-enabled-sql-server/activate-pcore-license/README.md
@@ -0,0 +1,54 @@
+---
+services: Azure SQL
+platforms: Azure
+author: anosov1960
+ms.author: sashan
+ms.date: 06/24/2024
+---
+
+# Overview
+
+This script performes a scheduled activation of a SQL Server p-core license.
+
+# Required permissions
+
+Your RBAC role must include the following permissions:
+
+- Microsoft.AzureArcData/SqlLicenses/read
+- Microsoft.AzureArcData/SqlLicenses/write
+- Microsoft.Management/managementGroups/read
+- Microsoft.Resources/subscriptions/read
+- Microsoft.Resources/subscriptions/resourceGroups/read
+- Microsoft.Support/supporttickets/write
+
+# Creating a Azure runbook
+
+You can scahedule to run the the command as a runbook. To set it up using Azure Portal, follow these steps.
+
+1. Open a command shell on your device and run this command. It will copy the script to your local folder.
+```console
+curl https://raw.githubusercontent.com/microsoft/sql-server-samples/master/samples/manage/azure-arc-enabled-sql-server/activate-pcore-license/activate-pcore-license.md -o activate-pcore-license.ps1
+```
+1. [Create a new automation account](https://ms.portal.azure.com/#create/Microsoft.AutomationAccount) or open an existing one. In the Advanced section, make sure that *System assigned identity* is selecetd.
+1. In the **Process automation** group select **Runbooks**
+1. Click on the *Import a runbook* tab and configure it:
+ * select the file you downloaded in Step 1
+ * type in a name of the runbook
+ * select the type as PowerShell
+ * select the runtime version 5.1
+ * click **Import**.
+1. When import is completed, click the *Publish* button.
+1. Once the status changes to *Published* on the runbook blade, click on the *Link to schedule* button
+1. Select *Link a schedule to your runbook* and click *+ Add a schedule*
+1. Configure the schedule:
+ * type in the name of the schedule
+ * set the start time
+ * set *Recurrance* to Once
+ * click **Create**
+1. In a separate browser window, open the Azure resource of your inactive licence, go to the *Settings* group, select *Properties* and copy the license ID (URI) to the clipboard.
+1. Go back to *Schedule runbook page*, click on *Parameters and run settings* and paste the license ID value you copied to the clipboard.
+1. Click **OK** to link to the schedule and **OK** again to create the job.
+1. On the runbook Overview page, click to open a recent job that was completed right after the specified start time.
+1. Click on the *Output* tab and notice the `Properties.activationState=Activated`. Your license is now active.
+
+For more information about the runbooks, see the [Runbook tutorial](https://docs.microsoft.com/en-us/azure/automation/learn/automation-tutorial-runbook-textual-powershell)
diff --git a/samples/manage/azure-arc-enabled-sql-server/activate-pcore-license/activate-pcore-license.md b/samples/manage/azure-arc-enabled-sql-server/activate-pcore-license/activate-pcore-license.md
new file mode 100644
index 0000000000..cb4b28fb47
--- /dev/null
+++ b/samples/manage/azure-arc-enabled-sql-server/activate-pcore-license/activate-pcore-license.md
@@ -0,0 +1,36 @@
+.<#
+ .DESCRIPTION
+ This runbook activates a SQL Server license using the Managed Identity
+ The runbook accepts the following parameters:
+
+ -LicenseID (The license resource URI)
+
+ .NOTES
+ AUTHOR: Alexander (Sasha) Nosov
+ LASTEDIT: June 24, 2024
+#>
+
+param (
+ [Parameter (Mandatory= $true)]
+ [string] $LicenseId
+)
+
+#
+# Suppress warnings
+#
+Update-AzConfig -DisplayBreakingChangeWarning $false
+
+# Logging in to Azure..."
+try
+{
+ Connect-AzAccount -Identity
+}
+catch {
+ Write-Error -Message $_.Exception
+ throw $_.Exception
+}
+
+$currentLicense = Get-AzResource -ResourceId $LicenseId
+$currentLicense.properties.activationState = "Activated"
+$currentLicense | Set-AzResource -Force
+
diff --git a/samples/manage/azure-arc-enabled-sql-server/install-payg-sql-server/README.md b/samples/manage/azure-arc-enabled-sql-server/install-payg-sql-server/README.md
new file mode 100644
index 0000000000..fd67a5b8e7
--- /dev/null
+++ b/samples/manage/azure-arc-enabled-sql-server/install-payg-sql-server/README.md
@@ -0,0 +1,69 @@
+# Overview
+
+This script installs a pay-as-you-go SQL Server instance on your machine and automatically connects it to Azure using a downloaded SQL Server media.
+
+# Prerequisites
+
+- You have met the [onboarding prerequisites](https://learn.microsoft.com/sql/sql-server/azure-arc/prerequisites).
+- You have downloaded a SQL Server image file from the workspace provided by Microsoft technical support. To obtain it, open a support request using the "Get SQL Installation Media" subcategory and specify the desired version and edition.
+- You are logged in to the machine with an administrator account.
+- If you are installing SQL Server on Windows Server 2016, you have a secure TLS configuration as described below.
+
+
+# Mitigating the TLS version issue on Windows Server 2016
+
+When running the script on Windows Server 2016, the OS may be configured with a TLS version that does not meet the Azure security requirements. You need to enable strong TLS versions (TLS 1.2 and 1.3) when they are available, while still supporting older TLS versions (1.0 and 1.1) when TLS 1.2 and 1.3 are unavailable. You need to also disable versions SSL2 and SSL3, which are insecure.
+
+To see if you need to make the change, run the command below from an elevated PowerShell prompt.
+```PowerShell
+[Net.ServicePointManager]::SecurityProtocol
+```
+
+If the result is `SSL3, Tls`, you need to fix the TLS version by running the following commands.
+
+```PowerShell
+Set-ItemProperty -Path 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NetFramework\v4.0.30319' -Name 'SchUseStrongCrypto' -Value '1' -Type DWord
+Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\.NetFramework\v4.0.30319' -Name 'SchUseStrongCrypto' -Value '1' -Type DWord
+```
+
+After running these commands, reboot the machine (in case currently-running applications were referencing previous values). To verify that the changes were applied correctly, run this command again:
+
+```PowerShell
+[Net.ServicePointManager]::SecurityProtocol
+```
+The result should be `Tls, Tls11, Tls12, Tls13`
+
+# Downloading the script
+
+To download the script to your current folder run:
+
+```console
+curl https://raw.githubusercontent.com/microsoft/sql-server-samples/master/samples/manage/azure-arc-enabled-sql-server/install-payg-sql-server/install-payg-sql-server.ps1 -o install-payg-sql-server.ps1
+```
+
+# Launching the script
+
+The script must be run in an elevated PowerShell session. It accepts the following command line parameters:
+
+| **Parameter** | **Value** | **Description** |
+|:--|:--|:--|
+|-AzureSubscriptionId|subscription_id|Required: Subscription id that will contain the Arc-enabled machine and Arc-enable SQL Server resources. That subscription will be billed for SQL Server software using a pay-as-you-go method. |
+|-AzureResourceGroup |resource_group_name|Required: Resource group that will contain the Arc-enabled machine and Arc-enable SQL Server resource.|
+|-AzureRegion |region name| Required: the region to store the machuine and SQL Server meta-data. |
+|-SqlServerInstanceName | name of the instance|Optional: the machine name will be used if not specified|
+|-SqlServerAdminAccounts | SQL Server admin accounts | Optional. By default "BUILTIN\ADMINISTRATORS" will be used.|
+|-SqlServerSvcAccount| SQL Server services account |Optional. By default "NT AUTHORITY\NETWORK SERVICE" will be used.|
+|-SqlServerSvcPassword| SQL Server service account password| Required if a custom service account is specified.|
+|-AgtServerSvcAccount|SQL Agent service account|Optional. By default "NT AUTHORITY\NETWORK SERVICE" will be used.|
+|-AgtServerSvcPassword|SQL Agent service account password|Required if a custom service account is specified.|
+|-IsoFolder|Folder path|Required. The folder containing the files downloaded from the workspace.|
+|-Proxy|HTTP proxy URL|Optional. Needed if your networks is configured with an HTTP proxy.|
+
+# Example
+
+The following command installs a SQL Server instance from the Downloads folder, connects it to subscription ID ``, resource group `` in the West US region, and configures it with LicenseType=PAYG. It uses the default admin and service accounts, and uses a direct connection to Azure.
+
+```PowerShell
+.\install-payg-sql-server.ps1 -AzureSubscriptionId -AzureResourceGroup -AzureRegion westus -IsoFolder C:\Users\[YourUsername]\Downloads
+
+```
diff --git a/samples/manage/azure-arc-enabled-sql-server/install-payg-sql-server/install-payg-sql-server.ps1 b/samples/manage/azure-arc-enabled-sql-server/install-payg-sql-server/install-payg-sql-server.ps1
new file mode 100644
index 0000000000..0a4efb6353
--- /dev/null
+++ b/samples/manage/azure-arc-enabled-sql-server/install-payg-sql-server/install-payg-sql-server.ps1
@@ -0,0 +1,309 @@
+param (
+ [Parameter (Mandatory=$true)]
+ [string]$AzureSubscriptionId,
+ [Parameter (Mandatory=$true)]
+ [string]$AzureResourceGroup,
+ [Parameter (Mandatory=$true)]
+ [string]$AzureRegion,
+ [Parameter (Mandatory=$false)]
+ [string]$SqlServerInstanceName,
+ [Parameter (Mandatory=$false)]
+ [string]$SqlServerAdminAccounts = "BUILTIN\ADMINISTRATORS",
+ [Parameter (Mandatory=$false)]
+ [string]$SqlServerSvcAccount = "NT AUTHORITY\NETWORK SERVICE",
+ [Parameter (Mandatory=$false)]
+ [string]$SqlServerSvcPassword,
+ [Parameter (Mandatory=$false)]
+ [string]$AgtServerSvcAccount = "NT AUTHORITY\NETWORK SERVICE",
+ [Parameter (Mandatory=$false)]
+ [string]$AgtServerSvcPassword,
+ [Parameter (Mandatory=$true)]
+ [string]$IsoFolder,
+ [Parameter (Mandatory=$false)]
+ [string]$Proxy
+)
+
+# This function checks if the specified module is imported into the session and if not installes and/or imports it
+function LoadModule
+{
+ param (
+ [parameter(Mandatory = $true)][string] $name
+ )
+
+ $retVal = $true
+
+ if (!(Get-Module -Name $name))
+ {
+ $retVal = Get-Module -ListAvailable | Where-Object {$_.Name -eq $name}
+
+ if ($retVal)
+ {
+ try
+ {
+ Import-Module $name -ErrorAction SilentlyContinue
+ }
+ catch
+ {
+ write-host "The request to lload module $($name) failed with the following error:"
+ write-host $_.Exception.Message
+ $retVal = $false
+ }
+ }
+ else {
+
+ # If module is not imported, not available on disk, but is in online gallery then install and import
+ if (Find-Module -Name $name) {
+ Install-Module -Name $name -Force -Scope CurrentUser
+ try
+ {
+ Import-Module $name -ErrorAction SilentlyContinue
+ }
+ catch
+ {
+ write-host "The request to lload module $($name) failed with the following error:"
+ write-host $_.Exception.Message
+ $retVal = $false
+ }
+ }
+ else {
+
+ # If module is not imported, not available and not in online gallery then abort
+ write-host "Module $($name) not imported, not available and not in online gallery, exiting."
+ EXIT 1
+ }
+ }
+ }
+
+ return $retVal
+}
+
+try {
+
+ write-host "==== Ensure PS version and load missing Azure modules ===="
+ #
+ # Suppress warnings
+ #
+ Update-AzConfig -DisplayBreakingChangeWarning $false
+
+ # Load required modules
+ $requiredModules = @(
+ "AzureAD",
+ "Az.Accounts",
+ "Az.Resources",
+ "Az.ConnectedMachine",
+ "Az.ResourceGraph"
+ )
+ $requiredModules | Foreach-Object {LoadModule $_}
+
+ write-host "==== Check if setup.exe is already running and kill it if so ===="
+
+ if (Get-Process setup -ErrorAction SilentlyContinue) {
+ Stop-Process -Name setup -Force
+ Write-Host "Existing setup.exe process terminated."
+ }
+
+ write-host "==== Log in to Azure ===="
+
+ Update-AzConfig -EnableLoginByWam $false
+ Connect-AzAccount | Out-Null
+ $subscription = Get-AzSubscription -SubscriptionId $AzureSubscriptionId -ErrorAction SilentlyContinue
+ if (-not $subscription) {
+ Write-Error "Azure subscription with ID '$AzureSubscriptionId' does not exist."
+ exit
+ }
+ Set-AzContext -Subscription $AzureSubscriptionId | Out-Null
+
+ write-host "==== Block auto-onboarding to Arc ===="
+
+ $existingResourceGroup = Get-AzResourceGroup -Name $AzureResourceGroup -ErrorAction SilentlyContinue
+
+ if ($existingResourceGroup) {
+ $tags = @{"ArcOnboarding" = "Blocked"}
+ Set-AzResourceGroup -Name $AzureResourceGroup -Tag $tags | Out-Null
+ } else {
+ Write-Error "Resource group '$AzureResourceGroup' does not exist."
+ exit
+ }
+
+ write-host "==== Mount the ISO file as a volume ===="
+
+ # Retrieve the product key if any
+
+ $keyFiles = (Get-ChildItem $IsoFolder -Filter "*.txt")
+ $productKey = ""
+ foreach ($keyFile in $keyFiles) {
+ # Read each line from the file
+ Get-Content $keyFile.fullname | ForEach-Object {
+ if ($_ -match "[A-Z0-9]{5,}-[A-Z0-9]{5,}-[A-Z0-9]{5,}-[A-Z0-9]{5,}-[A-Z0-9]{4,}.*") {
+ # Extract the product key (including any following string after a space)
+ $productKey = [regex]::Match($_, '[A-Z0-9]{5,}-[A-Z0-9]{5,}-[A-Z0-9]{5,}-[A-Z0-9]{5,}-[A-Z0-9]{4,}.*').Value
+ # Strip any text after the product key
+ $productKey = $productKey -replace " .*$"
+ }
+ }
+ }
+
+
+ $isoFiles = (Get-ChildItem $IsoFolder -Filter "*.iso")
+
+
+ # Pick the .iso file and mount
+
+ $noKeylist = "SQLFULL_ENU_ENTVL.iso", "SQLFULL_ENU_STDVL.iso", "SQLServer2022-x64-ENU-Ent.iso", "SQLServer2022-x64-ENU-Std.iso"
+
+ foreach ($isoFile in $isoFiles) {
+ write-host("****isoFile: $($isoFile)")
+ $imagePath = $isoFile.FullName
+ if ($noKeylist -contains $isoFile.Name) {$productKey = ""}
+ if (!(Get-DiskImage -ImagePath $imagePath).Attached) {
+ $mountResult = Mount-DiskImage -ImagePath $imagePath -PassThru
+ } else {
+ $mountResult = Get-DiskImage -ImagePath $imagePath
+ }
+ $driveLetter = ($mountResult | Get-Volume).DriveLetter
+ Write-Host "ISO file $($isoFile.Name) mounted as drive $($driveLetter):"
+ break
+ }
+
+
+ write-host "==== Run unattended SQL Server setup from the mounted volume ===="
+
+
+ # Launch setup
+
+ $setupPath = ($driveLetter + ":\setup.exe")
+
+
+ $argumentList = "/q /ACTION=Install /FEATURES=SQL /SQLSVCACCOUNT=`"$($SqlServerSvcAccount)`" /SQLSYSADMINACCOUNTS=`"$($SqlServerAdminAccounts)`" /AGTSVCACCOUNT=`"$($AgtServerSvcAccount)`" /IACCEPTSQLSERVERLICENSETERMS"
+ # some optional arguments
+ if ($SqlServerInstanceName) {
+ $argumentList += " /INSTANCENAME= $($SqlServerInstanceName)"
+ }
+ if ($productKey) {
+ $argumentList += " /PID=`"$($productKey)`""
+ }
+
+ if ($SqlServerSvcPassword) {
+ $argumentList += " /SQLSVCPASSWORD=`"$($SqlServerSvcPassword)`""
+ }
+
+ if ($AgtSvCPassword) {
+ $argumentList += " /AGTSVCPASSWORD=`"$($AgtSvCPassword)`""
+ }
+
+
+ Start-Process -Wait -FilePath $setupPath -ArgumentList $argumentList -RedirectStandardOutput setup-output.txt
+
+
+ Dismount-DiskImage -ImagePath $imagePath | Out-Null
+
+ write-host "==== Onboard the VM to Azure Arc ===="
+
+ $hostName = (Get-WmiObject Win32_ComputerSystem).Name
+
+ if ($Proxy) {
+ Connect-AzConnectedMachine -ResourceGroupName $AzureResourceGroup -Name $hostName -Location $AzureRegion -Proxi $Proxy | Out-Null
+ } else {
+ Connect-AzConnectedMachine -ResourceGroupName $AzureResourceGroup -Name $hostName -Location $AzureRegion | Out-Null
+ }
+
+ write-host "==== Install SQL Arc extension with LT=PAYG and upgrade to the latest version ===="
+
+
+ $Settings = @{
+ SqlManagement = @{ IsEnabled = $true };
+ LicenseType = "PAYG";
+ enableExtendedSecurityUpdates = $False;
+ }
+
+
+ New-AzConnectedMachineExtension -ResourceGroupName $AzureResourceGroup -MachineName $hostName -Name "WindowsAgent.SqlServer" -Publisher "Microsoft.AzureData" -ExtensionType "WindowsAgent.SqlServer" -Location $AzureRegion -Settings $Settings -EnableAutomaticUpgrade
+
+
+ write-host "==== Display the status of the billable Arc-enabled host ===="
+
+ $query = "
+ resources
+ | where type =~ 'Microsoft.HybridCompute/machines'
+ | where subscriptionId =~ '$($AzureSubscriptionId)'
+ | where resourceGroup =~ '$($AzureResourceGroup)'
+ | extend status = tostring(properties.status)
+ | where status =~ 'Connected'
+ | extend machineID = tolower(id)
+ | extend VMbyManufacturer = toboolean(iff(properties.detectedProperties.manufacturer in (
+ 'VMware',
+ 'QEMU',
+ 'Amazon EC2',
+ 'OpenStack',
+ 'Hetzner',
+ 'Mission Critical Cloud',
+ 'DigitalOcean',
+ 'UpCloud',
+ 'oVirt',
+ 'Alibaba',
+ 'KubeVirt',
+ 'Parallels',
+ 'XEN'
+ ), 1, 0))
+ | extend VMbyModel = toboolean(iff(properties.detectedProperties.model in (
+ 'OpenStack',
+ 'Droplet',
+ 'oVirt',
+ 'Hypervisor',
+ 'Virtual',
+ 'BHYVE',
+ 'KVM'
+ ), 1, 0))
+ | extend GoogleVM = toboolean(iff((properties.detectedProperties.manufacturer =~ 'Google') and (properties.detectedProperties.model =~ 'Google Compute Engine'), 1, 0))
+ | extend NutanixVM = toboolean(iff((properties.detectedProperties.manufacturer =~ 'Nutanix') and (properties.detectedProperties.model =~ 'AHV'), 1, 0))
+ | extend MicrosoftVM = toboolean(iff((properties.detectedProperties.manufacturer =~ 'Microsoft Corporation') and (properties.detectedProperties.model =~ 'Virtual Machine'), 1, 0))
+ | extend billableCores = iff(VMbyManufacturer or VMbyModel or GoogleVM or NutanixVM or MicrosoftVM, properties.detectedProperties.logicalCoreCount, properties.detectedProperties.coreCount)
+ | join kind = leftouter // Join Extension
+ (
+ resources
+ | where type =~ 'Microsoft.HybridCompute/machines/extensions'
+ | where name == 'WindowsAgent.SqlServer' or name == 'LinuxAgent.SqlServer'
+ | extend extMachineID = substring(id, 0, indexof(id, '/extensions'))
+ | extend extensionId = id
+ )
+ on `$left.id == `$right.extMachineID
+ | join kind = inner // Join SQL Arc
+ (
+ resources
+ | where type =~ 'microsoft.azurearcdata/sqlserverinstances'
+ | extend sqlVersion = tostring(properties.version)
+ | extend sqlEdition = tostring(properties.edition)
+ | extend is_Enterprise = toint(iff(sqlEdition == 'Enterprise', 1, 0))
+ | extend sqlStatus = tostring(properties.status)
+ | extend licenseType = tostring(properties.licenseType)
+ | where sqlEdition in ('Enterprise', 'Standard')
+ | where licenseType !~ 'HADR'
+ | where sqlStatus =~ 'Connected'
+ | extend ArcServer = tolower(tostring(properties.containerResourceId))
+ | order by sqlEdition
+ )
+ on `$left.machineID == `$right.ArcServer
+ | where isnotnull(extensionId)
+ | summarize Edition = iff(sum(is_Enterprise) > 0, 'Enterprise', 'Standard') by machineID
+ , name
+ , resourceGroup
+ , subscriptionId
+ , Model = tostring(properties.detectedProperties.model)
+ , Manufacturer = tostring(properties.detectedProperties.manufacturer)
+ , License_Type = tostring(properties1.settings.LicenseType)
+ , OS = tostring(properties.osName)
+ , Uses_UV = tostring(properties1.settings.UsePhysicalCoreLicense.IsApplied)
+ , Cores = tostring(billableCores)
+ , Version = sqlVersion
+ | project-away machineID
+ | order by Edition, name asc
+ "
+ Search-AzGraph -Query $query
+
+} catch {
+ Write-Error "An error occurred: $_"
+ # You can add additional error handling logic here
+} finally {
+ # Cleanup or other actions that should always run
+ Write-Host "==== Installation completed ===="
+}
diff --git a/samples/manage/azure-arc-enabled-sql-server/modify-license-type/README.md b/samples/manage/azure-arc-enabled-sql-server/modify-license-type/README.md
index e0282d4038..2a86d27636 100644
--- a/samples/manage/azure-arc-enabled-sql-server/modify-license-type/README.md
+++ b/samples/manage/azure-arc-enabled-sql-server/modify-license-type/README.md
@@ -3,7 +3,7 @@ services: Azure Arc-enabled SQL Server
platforms: Azure
author: anosov1960
ms.author: sashan
-ms.date: 9/22/2023
+ms.date: 05/01/2025
---
@@ -27,12 +27,14 @@ The script accepts the following command line parameters:
| **Parameter** | **Value** | **Description** |
|:--|:--|:--|
-|-SubId|subscription_id *or* a file_name|Optional: Subscription id or a .csv file with the list of subscriptions1. If not specified all subscriptions will be scanned|
-|-ResourceGroup |resource_group_name|Optional: Limits the scope to a specific resource group|
-|-MachineName |machine_name|Optional: Limits the scope to a specific machine|
-|-LicenseType | "Paid", "PAYG" or "LicenseOnly"| Optional: Sets the license type to the specified value |
-|-EnableESU | "Yes", "No" | Optional. Enables the ESU policy the value is "Yes" or disables it if the value is "No". To enable, the license type must be "Paid" or "PAYG"|
-|-Force| |Optional. Forces the change of the license type to the specified value on all installed extensions. If -Force is not specified, the -LicenseType value is set only if undefined. Ignored if -LicenseType is not specified|
+|`-SubId`|subscription_id *or* a file_name|*Optional*: Subscription id or a .csv file with the list of subscriptions1. If not specified all subscriptions will be scanned|
+|`-ResourceGroup` |resource_group_name|*Optional*: Limits the scope to a specific resource group|
+|`-MachineName` |machine_name|*Optional*: Limits the scope to a specific machine|
+|`-LicenseType` | "Paid", "PAYG" or "LicenseOnly"| *Optional*: Sets the license type to the specified value |
+|`-ConsentToRecurringPAYG` | "Yes" or "No" |*Optional*. Consents to enabling the recurring PAYG billing. LicenseType must be "PAYG". Applies to CSP subscriptions only.|
+|`-UsePcoreLicense` | "Yes", "No" | *Optional*. Enables unlimited virtualization license if the value is "Yes" or disables it if the value is "No". To enable, the license type must be "Paid" or "PAYG"|
+|`-EnableESU` | "Yes", "No" | *Optional*. Enables the ESU policy the value is "Yes" or disables it if the value is "No". To enable, the license type must be "Paid" or "PAYG"|
+|`-Force`| |*Optional*. Forces the change of the license type to the specified value on all installed extensions. If `-Force` is not specified, the `-LicenseType` value is set only if undefined. Ignored if `-LicenseType` is not specified|
1You can create a .csv file using the following command and then edit to remove the subscriptions you don't want to scan.
```PowerShell
@@ -57,15 +59,15 @@ The following command will scan the subscription `` and set the license
## Example 3
-The following command will scan resource group `` in the subscription `` and set the license type value to "PAYG" on all servers.
+The following command will scan resource group `` in the subscription ``, set the license type value to "PAYG" and enable unlimited virtualization license on all servers in the specified resource group.
```PowerShell
-.\modify-license-type.ps1 -SubId -ResourceGroup -LicenseType PAYG -Force
+.\modify-license-type.ps1 -SubId -ResourceGroup -LicenseType PAYG -UsePcoreLicense Yes -Force
```
## Example 4
-The following command will set License Type to 'Paid" and enables ESU on all servers in the subscriptions `` and the resource group ``.
+The following command will set License Type to "Paid" and enables ESU on all servers in the subscriptions `` and the resource group ``.
```console
.\modify-license-type.ps1 -SubId -ResourceGroup -LicenseType Paid -EnableESU Yes -Force
@@ -79,6 +81,16 @@ The following command will disable ESU on all servers in the subscriptions ` -EnableESU No
```
+## Example 6
+
+The following command will scan all subscriptions in the account, set the license type value to "PAYG" and consents to enabling recurring billing on all servers in the account.
+
+```PowerShell
+.\modify-license-type.ps1 -LicenseType PAYG -ConsentToRecurringPAYG Yes -Force
+```
+> [!NOTE]
+> The recurring billing only supported in the CSP accounts.
+
# Running the script using Cloud Shell
This option is recommended because Cloud shell has the Azure PowerShell modules pre-installed and you are automatically authenticated. Use the following steps to run the script in Cloud Shell.
diff --git a/samples/manage/azure-arc-enabled-sql-server/modify-license-type/modify-license-type.ps1 b/samples/manage/azure-arc-enabled-sql-server/modify-license-type/modify-license-type.ps1
index 8c7e8a3fe8..62e686655c 100644
--- a/samples/manage/azure-arc-enabled-sql-server/modify-license-type/modify-license-type.ps1
+++ b/samples/manage/azure-arc-enabled-sql-server/modify-license-type/modify-license-type.ps1
@@ -6,13 +6,16 @@
# If not specified, all subscriptions your role has access to are scanned.
#
# The script accepts the following command line parameters:
-#
+#.
# -SubId [subscription_id] | [csv_file_name] (Optional. Limits the scope to specific subscriptions. Accepts a .csv file with the list of subscriptions.
# If not specified all subscriptions will be scanned)
# -ResourceGroup [resource_goup] (Optional. Limits the scope to a specific resoure group)
# -MachineName [machine_name] (Optional. Limits the scope to a specific machine)
# -LicenseType [license_type_value] (Optional. Sets the license type to the specified value)
-# -EnabelESU [Yes or No] (Optional. Enables the ESU policy the value is "Yes" or disables it if the value is "No"
+# -ConsentToRecurringPAYG [Yes or No] (Optional. Consents to enabling the recurring PAYG billing. LicenseType must be "PAYG". Applies to CSP subscriptions only.
+# -UsePcoreLicense [Yes or No] (Optional. Enables unlimited virtualization license if the value is "Yes" or disables it if the value is "No"
+# To enable, the license type must be "Paid" or "PAYG"
+# -EnableESU [Yes or No] (Optional. Enables the ESU policy if the value is "Yes" or disables it if the value is "No"
# To enable, the license type must be "Paid" or "PAYG"
# -Force (Optional. Forces the chnahge of the license type to the specified value on all installed extensions.
# If Force is not specified, the -LicenseType value is set only if undefined. Ignored if -LicenseType is not specified
@@ -32,6 +35,11 @@ param (
[Parameter (Mandatory= $false)]
[ValidateSet("PAYG","Paid","LicenseOnly", IgnoreCase=$false)]
[string] $LicenseType,
+ [ValidateSet("Yes","No", IgnoreCase=$false)]
+ [string] $ConsentToRecurringPAYG,
+ [Parameter (Mandatory= $false)]
+ [ValidateSet("Yes","No", IgnoreCase=$false)]
+ [string] $UsePcoreLicense,
[Parameter (Mandatory= $false)]
[ValidateSet("Yes","No", IgnoreCase=$false)]
[string] $EnableESU,
@@ -115,7 +123,7 @@ function LoadModule
}
catch
{
- write-host "The request to lload module $($name) failed with the following error:"
+ write-host "The request to load module $($name) failed with the following error:"
write-host $_.Exception.Message
$retVal = $false
}
@@ -229,25 +237,52 @@ foreach ($sub in $subscriptions){
$settings["LicenseType"] = $LicenseType
$WriteSettings = $true
}
- }
-
+ }
}
# Enable ESU for qualified license types or disable
if ($EnableESU) {
if (($settings["LicenseType"] | select-string "Paid","PAYG") -or ($EnableESU -eq "No")) {
$settings["enableExtendedSecurityUpdates"] = ($EnableESU -eq "Yes")
- $settings["esuLastUpdatedTimestamp"] = [DateTime]::UtcNow.ToString('yyyy-MM-ddTHH:mm:ss.fffZ')
+ $settings["esuLastUpdatedTimestamp"] = [DateTime]::UtcNow.ToString('yyyy-MM-ddTHH:mm:ssZ')
+ $WriteSettings = $true
+ } else {
+ write-host "The configured license type does not support ESUs"
+ }
+ }
+
+ # Enable UsePcoreLicense for qualified license types or disable
+ if ($UsePcoreLicense) {
+ if (($settings["LicenseType"] | select-string "Paid","PAYG") -or ($UsePcoreLicense -eq "No")) {
+ $settings["UsePhysicalCoreLicense"] = @{
+ "IsApplied" = ($UsePcoreLicense -eq "Yes");
+ "LastUpdatedTimestamp" = [DateTime]::UtcNow.ToString('yyyy-MM-ddTHH:mm:ssZ')
+ }
$WriteSettings = $true
} else {
write-host "The configured license type does not support ESUs"
}
}
+ # Add or update ConsentToRecurringPAYG setting if applicable
+ if ($ConsentToRecurringPAYG -eq "Yes") {
+ $isPayg = ($LicenseType -eq "PAYG") -or ($settings["LicenseType"] -eq "PAYG")
+ if ($isPayg) {
+ if (-not $settings.ContainsKey("ConsentToRecurringPAYG") -or -not $settings["ConsentToRecurringPAYG"]["Consented"]) {
+ $settings["ConsentToRecurringPAYG"] = @{
+ "Consented" = $true;
+ "ConsentTimestamp" = [DateTime]::UtcNow.ToString('yyyy-MM-ddTHH:mm:ssZ')
+ }
+ $WriteSettings = $true
+ }
+ }
+ }
+
If ($WriteSettings) {
- Write-Host "Resource group: [$($r.resourceGroup)] Connected machine: [$($r.MachineName)] : License type: [$($settings["LicenseType"])] : Enable ESU: [$($settings["enableExtendedSecurityUpdates"])]"
+
try {
Set-AzConnectedMachineExtension @setId -Settings $settings -NoWait | Out-Null
+ Write-Host "Updated -- Resource group: [$($r.resourceGroup)], Connected machine: [$($r.MachineName)]"
} catch {
write-host "The request to modify the extenion object failed with the following error:"
write-host $_.Exception.Message
@@ -257,4 +292,4 @@ foreach ($sub in $subscriptions){
}
}
-
\ No newline at end of file
+
diff --git a/samples/manage/azure-hybrid-benefit/get-sqlvm-inventory.ps1 b/samples/manage/azure-hybrid-benefit/get-sqlvm-inventory.ps1
index 591cd8f04c..94bf5b71db 100644
--- a/samples/manage/azure-hybrid-benefit/get-sqlvm-inventory.ps1
+++ b/samples/manage/azure-hybrid-benefit/get-sqlvm-inventory.ps1
@@ -1,32 +1,10 @@
-#
-# This script scan each VM in scope to detect if SQL server insstalled and if it is regsitered with IaaS extension.
-# The report includes the following information for each VM.
-#
-# Subscription Name
-# Subscription ID
-# ResourceGroupName
-# Server Name
-# Location
-# Server SKU
-# Size in vCores
-# OS Type
-# OS Version
-# SQL Version
-# SQL Edition
-# IaaS registration status
-#
-# The script accepts the following command line parameters:
-#
-# -SubId [subscription_id] (Optional. If not specified all subscription the user has access to Accepts a .csv file with the list of subscriptions)
-# -FilePath [csv_file_name] (Optional. Sprcifies a .csv file to save the data. if not specified, saves it in sql-vcm-inventory.csv)
-#
-
-param (
+param (
[Parameter (Mandatory= $false)]
[string] $SubId,
[Parameter (Mandatory= $false)]
+ [PSCredential] $Cred,
+ [Parameter (Mandatory= $false)]
[string] $FilePath
-
)
function CheckModule ($m) {
@@ -56,76 +34,10 @@ function CheckModule ($m) {
}
}
-function GetVCores {
- # This function translates each VM or Host sku type and name into vCores
-
- [CmdletBinding()]
- param (
- [Parameter(Mandatory)]
- [string]$type,
- [Parameter(Mandatory)]
- [string]$name
- )
-
- if ($global:VM_SKUs.Count -eq 0){
- $global:VM_SKUs = Get-AzComputeResourceSku "westus" | where-object {$_.ResourceType -in 'virtualMachines','hostGroups/hosts'}
- }
- # Select first size and get the VCPus available
- $size_info = $global:VM_SKUs | Where-Object {$_.ResourceType.Contains($type) -and ($_.Name -eq $name)} | Select-Object -First 1
-
- # Save the VCPU count
- switch ($type) {
- "hosts" {$vcpu = $size_info.Capabilities | Where-Object {$_.name -eq "Cores"} }
- "virtualMachines" {$vcpu = $size_info.Capabilities | Where-Object {$_.name -eq "vCPUsAvailable"} }
- }
-
- if ($vcpu){
- return $vcpu.Value
- }
- else {
- return 0
- }
- }
-
-function DiscoveryOnWindows {
-
-# This script checks if SQL Server is installed on Windows
-
- [string] $SqlInstalled = ""
- $regPath = 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server'
- if (Test-Path $regPath) {
- $inst = (get-itemproperty $regPath).InstalledInstances
- #$SqlInstalled = ($inst.Count -gt 0)
- foreach ($i in $inst) {
- # Read registry data
- #
- $p = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL').$i
- $setupValues = (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$p\Setup")
- $edition = ($setupValues.Edition -split ' ')[0]
- $version = ($setupValues.Version)
- $SqlInstalled = $version, $edition -join ':'
- }
- }
- Write-Output $SqlInstalled
-}
-
-#
-# This script checks if SQL Server is installed on Linux
#
+# Suppress warnings
#
-$DiscoveryOnLinux =
- 'if ! systemctl is-active --quiet mssql-server.service; then dir
-
- echo "False"
- exit
- else
- echo "True"
- fi'
-
-
-
-# Ensure that the required modules are imported
-# In Runbooks these modules must be added to the automation account manually
+Update-AzConfig -DisplayBreakingChangeWarning $false
$requiredModules = @(
"Az.Accounts",
@@ -137,37 +49,27 @@ $requiredModules = @(
)
$requiredModules | Foreach-Object {CheckModule $_}
-# Save the function definitions to run in parallel loops
-$GetVCoresDef = $function:GetVCores.ToString()
-
-# Create a script file with the SQL server discovery logic
-New-Item -ItemType file -path DiscoverSql.ps1 -value $function:DiscoveryOnWindows.ToString() -Force | Out-Null
-New-Item -ItemType file -path DiscoverSql.sh -value $DiscoveryOnLinux -Force | Out-Null
-
# Subscriptions to scan
-
if ($SubId -like "*.csv") {
$subscriptions = Import-Csv $SubId
-}elseif($SubId.length -gt 0){
+}elseif($SubId -ne $null){
$subscriptions = [PSCustomObject]@{SubscriptionId = $SubId} | Get-AzSubscription
}else{
$subscriptions = Get-AzSubscription
}
-
-#File setup
+#Log file setup
if (!$PSBoundParameters.ContainsKey("FilePath")) {
- $FilePath = '.\sql-vm-inventory.csv'
+ $FilePath = '.\sql-change-log.csv'
}
-[System.Collections.ArrayList]$inventoryTable = @()
-$inventoryTable += ,(@("Subscription Name", "Subscription ID", "ResourceGroupName", "Name", "Location", "SKU", "vCores", "OS Type", "OS Version", "SQL Version", "SQL Edition", "IaaS registration"))
-
-$global:VM_SKUs = @{} # To hold the VM SKU table for future use
-
Write-Host ([Environment]::NewLine + "-- Scanning subscriptions --")
+# Record the start time
+$startTime = Get-Date
+Write-Host ("Script execution started at: $startTime")
+
# Calculate usage for each subscription
foreach ($sub in $subscriptions){
@@ -178,78 +80,90 @@ foreach ($sub in $subscriptions){
Set-AzContext -SubscriptionId $sub.Id
}catch {
write-host "Invalid subscription: " $sub.Id
- {continue}
+ continue
}
- # Reset the subtotals
- #$subtotal.psobject.properties.name | Foreach-object {$subtotal.$_ = 0}
-
# Get all resource groups in the subscription
- #$rgs = Get-AzResourceGroup
-
- # Scan all VMs with SQL server installed using a parallel loop (up to 10 at a time).
- # NOTE: ForEach-Object -Parallel requires PS v7.1 or higher
- if ($PSVersionTable.PSVersion.Major -ge 7){
- #Get-AzVM -Status | Where-Object { $_.powerstate -eq 'VM running' } | ForEach-Object -ThrottleLimit 10 -Parallel {
- Get-AzVM -Status | Where-Object { $_.powerstate -eq 'VM running' } | ForEach-Object {
- #$function:GetVCores = $using:GetVCoresDef
- $SqlEdition = ''
- $SqlVersion = ''
- $vCores = GetVCores -type 'virtualMachines' -name $_.HardwareProfile.VmSize
- $sql_vm = Get-AzSqlVm -ResourceGroupName $_.ResourceGroupName -Name $_.Name -ErrorAction Ignore
-
-
- if ($sql_vm) {
- $RegStatus = 'SQL Server registered'
- $SqlEdition = $Sql_vm.Sku
- $SqlVersion = $Sql_vm.Offer
- }
- else {
- if ($_.StorageProfile.OSDisk.OSType -eq "Windows"){
- $params =@{
- ResourceGroupName = $_.ResourceGroupName
- Name = $_.Name
- CommandId = 'RunPowerShellScript'
- ScriptPath = 'DiscoverSql.ps1'
- ErrorAction = 'Stop'
- }
- }
- else {
- $params =@{
- ResourceGroupName = $_.ResourceGroupName
- Name = $_.Name
- CommandId = 'RunShellScript'
- ScriptPath = 'DiscoverSql.sh'
- ErrorAction = 'Stop'
- }
- }
- try {
- $out = Invoke-AzVMRunCommand @params
- if (!$out.Value[0].Message){
- $RegStatus = 'SQL Server not installed'
- }
- else {
- $SqlVersion, $SqlEdition = $out.Value[0].Message -split ':', 2
- $RegStatus = 'SQL Server not registered'
- }
- }
- catch {
- $RegStatus = 'No VM access'
- }
+ $rgs = Get-AzResourceGroup
+
+ # Get all logical servers
+ $servers = Get-AzSqlServer
+
+ # Scan all vCore-based SQL database resources in the subscription
+ $servers | Get-AzSqlDatabase | Where-Object { $_.SkuName -ne "ElasticPool" -and $_.Edition -in @("GeneralPurpose", "BusinessCritical", "Hyperscale") } | ForEach-Object {
+ if ($_.LicenseType -ne "LicenseIncluded") {
+ Set-AzSqlDatabase -ResourceGroupName $_.ResourceGroupName -ServerName $_.ServerName -DatabaseName $_.DatabaseName -LicenseType "LicenseIncluded"
+ Write-Host ([Environment]::NewLine + "-- Database $_.DatabaseName is set to \"LicenseIncluded\"")
+ }
+ }
+ [system.gc]::Collect()
+
+ # Scan all vCore-based SQL elastic pool resources in the subscription
+ $servers | Get-AzSqlElasticPool | Where-Object { $_.Edition -in @("GeneralPurpose", "BusinessCritical", "Hyperscale") } | ForEach-Object {
+ if ($_.LicenseType -ne "LicenseIncluded") {
+ Set-AzSqlElasticPool -ResourceGroupName $_.ResourceGroupName -ServerName $_.ServerName -ElasticPoolName $_.ElasticPoolName -LicenseType "LicenseIncluded"
+ Write-Host ([Environment]::NewLine + "-- ElasticPool $_.ElasticPoolName is set to \"LicenseIncluded\"")
+ }
+ }
+ [system.gc]::Collect()
+
+ # Scan all SQL managed instance resources in the subscription
+ Get-AzSqlInstance | Where-Object { $_.InstancePoolName -eq $null } | ForEach-Object {
+ if ($_.LicenseType -ne "LicenseIncluded") {
+ Set-AzSqlInstance -ResourceGroupName $_.ResourceGroupName -ServerName $_.ServerName -InstanceName $_.InstanceName -LicenseType "LicenseIncluded"
+ Write-Host ([Environment]::NewLine + "-- Instance $_.InstanceName is set to \"LicenseIncluded\"")
+ }
+ }
+ [system.gc]::Collect()
+
+ # Scan all instance pool resources in the subscription
+ Get-AzSqlInstancePool | Foreach-Object {
+ if ($_.LicenseType -ne "LicenseIncluded") {
+ Set-AzSqlInstancePool -ResourceGroupName $_.ResourceGroupName -ServerName $_.ServerName -InstanceName $_.InstanceName -LicenseType "LicenseIncluded"
+ Write-Host ([Environment]::NewLine + "-- InstancePool $_.InstanceName is set to \"LicenseIncluded\"")
+ }
+ }
+ [system.gc]::Collect()
+
+ # Scan all SSIS integration runtime resources in the subscription
+ $rgs | Get-AzDataFactoryV2 | Get-AzDataFactoryV2IntegrationRuntime | Where-Object { $_.State -eq "Started" -and $_.NodeSize -ne $null } | ForEach-Object {
+ if ($_.LicenseType -ne "LicenseIncluded") {
+ # Set the license type to "LicenseIncluded"
+ Set-AzDataFactoryV2IntegrationRuntime -ResourceGroupName $_.ResourceGroupName -DataFactoryName $_.DataFactoryName -Name $_.Name -LicenseType "LicenseIncluded"
+ Write-Host ([Environment]::NewLine + "-- DataFactory $_.DataFactoryName is set to \"LicenseIncluded\"")
+ }
+ }
+ [system.gc]::Collect()
+
+ # Scan all SQL VMs in the subscription
+ $rgs | Get-AzVM | Where-Object { $_.StorageProfile.ImageReference.Offer -like "*sql*" -and $_.ProvisioningState -eq "Succeeded" } | ForEach-Object {
+ $vmName = $_.Name
+ $resourceGroupName = $_.ResourceGroupName
+
+ # Get the SQL configuration for the VM
+ $sqlConfig = Get-AzVMExtension -ResourceGroupName $resourceGroupName -VMName $vmName -Name "SqlIaaSAgent"
+
+ if ($sqlConfig -ne $null) {
+ $licenseType = $sqlConfig.Settings.LicenseType
+
+ if ($licenseType -ne "LicenseIncluded") {
+ # Set the license type to "LicenseIncluded"
+ Set-AzVMExtension -ResourceGroupName $resourceGroupName -VMName $vmName -Name "SqlIaaSAgent" -Publisher "Microsoft.SqlServer.Management" -ExtensionType "SqlIaaSAgent" -TypeHandlerVersion "1.5" -Settings @{ "LicenseType" = "LicenseIncluded" }
+ Write-Host ([Environment]::NewLine + "-- SQL VM $vmName is set to \"LicenseIncluded\"")
}
- $inventoryTable += ,(@( $sub.Name, $sub.Id, $_.ResourceGroupName, $_.Name, $_.Location, $_.HardwareProfile.VmSize, $vCores, $_.StorageProfile.ImageReference.Offer, $_.StorageProfile.ImageReference.Sku, $SqlVersion, $SqlEdition, $RegStatus))
- #write-host $_.ResourceGroupName $_.Name $_.Location $_.HardwareProfile.VmSize $vCores $_.StorageProfile.ImageReference.Offer $_.StorageProfile.ImageReference.Sku $SqlVersion $SqlEdition $RegStatus
+
}
}
[system.gc]::Collect()
+
}
+# Record the end time
+$endTime = Get-Date
+Write-Host ("Script execution ended at: $endTime")
-# Write usage data to the .csv file
-if ($FilePath){
- (ConvertFrom-Csv ($inventoryTable | %{$_ -join ','})) | Export-Csv $FilePath -NoType #-Append
- Write-Host ([Environment]::NewLine + "-- Added the usage data to $FilePath --")
-} else {
- Write-Host $inventoryTable
-}
+# Calculate the duration of script execution
+$executionDuration = $endTime - $startTime
+Write-Host ("Script execution duration: $executionDuration")
+Write-Host ([Environment]::NewLine + "-- Script execution completed --")
diff --git a/samples/manage/azure-hybrid-benefit/modify-license-type/README.md b/samples/manage/azure-hybrid-benefit/modify-license-type/README.md
new file mode 100644
index 0000000000..8c5bdc84e0
--- /dev/null
+++ b/samples/manage/azure-hybrid-benefit/modify-license-type/README.md
@@ -0,0 +1,176 @@
+---
+services: Azure SQL
+platforms: Azure
+author: anosov1960,rodrigomonteiro-gbb
+ms.author: sashan.romontei
+ms.date: 04/08/2025
+---
+
+# Overview
+
+This script provides a scaleable solution to change the license type of various Azure SQL resources within the selected scope. It automates the process of modifying license settings for SQL Databases, Elastic Pools, SQL Managed Instances, SQL Instance Pools, SQL Virtual Machines, and DataFactory SSIS Integration Runtimes. The script supports targeting a single subscription, a list of subscriptions defined in a CSV file, or all accessible subscriptions. Optionally, it can also start resources that are stopped (if the -ForceStartOnResources parameter is enabled).
+
+This script is designed to help administrators standardize SQL licensing across their Azure environment by automating license updates. It accepts a subscription ID or CSV file (for a list of subscriptions). If no subscription is specified, it defaults to updating resources in all accessible subscriptions.
+
+# Target Resource Types
+
+- SQL Virtual Machines: Updates license types after checking if the VM is running. If not, it can optionally start the VM to perform the update.
+- SQL Managed Instances: Detects instances that are stopped or not in the "Ready" state and can force them to start before updating.
+- SQL Databases & Elastic Pools: Scans individual SQL servers to locate databases and elastic pools with a different license type and updates them accordingly.
+- SQL Instance Pools: Locates instance pools that require an update.
+- DataFactory SSIS Integration Runtimes: Checks for integration runtimes with an out-of-date license setting and updates them.
+
+# Required Permissions
+The automation account needs to have the bellow permissions in order to be able to successfully run the Runbook and update all the SQL Server resources license type:
+
+1. **SQL DB Contributor**: *SQL DB Contributor role*.
+1. **SQL Managed Instance Contributor**: *SQL Managed Instance Contributor*
+1. **SQL Server Contributor**: *SQL Managed Instance Contributor*
+1. **Data Factory Contributor**: *Data Factory Contributor role*.
+1. **Virtual Machine Contributor**: *Virtual Machine Contributor role*.
+
+A *Subscription Contributor* role has sufficient permissions to mdify any of the above resources.
+
+# Interactive Reporting
+
+The script logs steps along the process and compiles a final report summarizing which resources were updated in each category.
+
+# Integration with Azure Authentication
+
+The scripts is seamlessly integrated with Azure Authentication. It uses managed identity authentication (via Connect-AzAccount -Identity and az login --identity) to connect to your Azure environment securely.
+
+# Script parameters
+
+
+| **Parameter** | **Value** | **Description** |
+|:--|:--|:--|
+|-SubId|subscription_id *or* a file_name|Optional: Subscription id or a .csv file with the list of subscriptions1. If not specified all subscriptions will be scanned|
+|-ResourceGroup |resource_group_name|Optional: Limits the scope to a specific resource group|
+|-LicenseType | "LicenseIncluded" (default) or "BasePrice" | Optional: Sets the license type to the specified value |
+|-ForceStartOnResources| |(Optional) When enabled, the script will attempt to start SQL VMs and SQL Managed Instances if they are not running before applying the update.|
+
+# Logging & Error Handling
+
+The script logs key actions to the console and captures error messages using Write-Error. Check the console output for a summary report detailing which resources were updated.
+
+# Customizations
+
+You might want to customize the script’s logging or incorporate additional logging (e.g., writing to a file or Azure Log Analytics) to integrate seamlessly with your monitoring and reporting workflow.
+
+# Script execution examples
+
+## Example 1
+
+The following command will scan all the subscriptions to which the user has access to, and set the license type to pay-as-you-go on all SQL resources in each subscription that the user has access to.
+
+```PowerShell
+.\modify-license-type.ps1 -LicenseType LicenseIncluded
+```
+
+## Example 2
+
+The following command will scan a specific subscription, and set the license type to pay-as-you-go on all SQL resources.
+
+```PowerShell
+.\modify-license-type.ps1 -SubId -LicenseType LicenseIncluded
+```
+
+## Example 3
+
+The following command will scan the resource group `` in the subscription ``, set the license type value to pay-as-you-go. If the resource group has SQL VMs in the offline state, it will start each VM before applying the change, and then stop it.
+
+```PowerShell
+.\modify-license-type.ps1 -SubId -ResourceGroup -LicenseType LicenseIncluded -ForceStartOnResources
+```
+
+# Running the script from your PC
+
+1. Connect to Azure AD. You must specify `` if you have access to more than one AAD tenants.
+
+ ```console
+ Connect-AzureAD -TenantID
+ ```
+1. Open a command shell on your device and download the script the script to your local folder.
+
+```console
+curl https://raw.githubusercontent.com/microsoft/sql-server-samples/refs/heads/master/samples/manage/azure-hybrid-benefit/modify-license-type/modify-license-type.ps1
+```
+1. Execute the command as shown by the examples
+
+# Running the script using Cloud Shell
+
+This option is recommended because Cloud shell has the Azure PowerShell modules pre-installed and you are automatically authenticated. Use the following steps to run the script in Cloud Shell.
+
+1. Launch the [Cloud Shell](https://shell.azure.com/). For details, [read more about PowerShell in Cloud Shell](https://aka.ms/pscloudshell/docs).
+
+1. Connect to Azure AD. You must specify `` if you have access to more than one AAD tenants.
+
+ ```console
+ Connect-AzureAD -TenantID
+ ```
+
+1. Upload the script to your cloud shell using the following command:
+
+ ```console
+ curl https://raw.githubusercontent.com/microsoft/sql-server-samples/refs/heads/master/samples/manage/azure-hybrid-benefit/modify-license-type/modify-license-type.ps1
+ ```
+
+1. Execute the command as shown by the examples.
+
+> [!NOTE]
+> - To paste the commands into the shell, use `Ctrl-Shift-V` on Windows or `Cmd-v` on MacOS.
+> - The script will be uploaded directly to the home folder associated with your Cloud Shell session.
+
+# Schedule the script execution using Azure Runbook
+
+You can schedule the command to run as a runbook. Follow these steps using the Azure Portal:
+
+### 1. Download the Script
+
+Open a command shell on your device and dowload the script to to your current folder.
+
+```console
+curl https://raw.githubusercontent.com/microsoft/sql-server-samples/refs/heads/master/samples/manage/azure-hybrid-benefit/modify-license-type/modify-license-type.ps1
+```
+### 2. Create or Use an Existing Automation Account
+[Create a new automation account](https://ms.portal.azure.com/#create/Microsoft.AutomationAccount) or open an existing one. In the Advanced section, ensure that System assigned identity is selected.
+
+### 3. Import the Runbook
+- Navigate to the Process Automation group and select **Runbooks**.
+
+- Click on the **Import a runbook** tab and configure it:
+
+ **File**: Select the file you downloaded in Step 1.
+ **Name**: Enter a name for the runbook.
+ **Type**: Set to PowerShell.
+ **Runtime Version**: Choose 7.2.
+
+- Click **Import**.
+
+### 4. Publish the Runbook
+After the runbook is imported, click the **Publish** button to make it available for scheduling.
+
+### 5. Link the Runbook to a Schedule
+
+- Once the runbook status is *Published*, click on the **Link to schedule** button.
+- Select *Link a schedule to your runbook* and click **+ Add**.
+- Configure the schedule:
+ **Name**: Provide a name for the schedule.
+ **Start Time**: Set the desired start time.
+ **Recurrence**: Choose the recurrence need it.
+- Click **Create**.
+
+### 6. Configure Runbook Parameters
+- Return to the **Schedule runbook** page.
+- Click on **Parameters** and run settings.
+- Paste the license type value into the appropriate field.
+- Click **OK** to link the schedule, then **OK** again to create the job.
+
+### 7. Verify the Runbook Execution
+On the runbook Overview page:
+- Open a recent job that was completed after the scheduled start time.
+- Click on the **Output tab** and verify that you see: `Properties.activationState=Activated `
+
+Your license is now active.
+
+For more information about the runbooks, see the [Runbook tutorial](https://docs.microsoft.com/en-us/azure/automation/learn/automation-tutorial-runbook-textual-powershell)
\ No newline at end of file
diff --git a/samples/manage/azure-hybrid-benefit/modify-license-type/modify-license-type.ps1 b/samples/manage/azure-hybrid-benefit/modify-license-type/modify-license-type.ps1
new file mode 100644
index 0000000000..4aa1c9e0a1
--- /dev/null
+++ b/samples/manage/azure-hybrid-benefit/modify-license-type/modify-license-type.ps1
@@ -0,0 +1,442 @@
+<#
+.SYNOPSIS
+ Updates the license type for Azure SQL resources (SQL DBs, Elastic Pools, Managed Instances, Instance Pools, SQL VMs)
+ to a specified model ("LicenseIncluded" or "BasePrice"). Optionally starts resources if needed.
+
+.DESCRIPTION
+ The script updates Azure SQL License types across subscriptions by modifying the license settings for a variety of SQL resources. It supports processing resources in one of the following ways:
+
+Single Subscription:
+Run against a specified subscription ID.
+CSV List of Subscriptions:
+Process multiple subscriptions provided in a CSV file.
+All Accessible Subscriptions:
+Automatically detect and update all subscriptions that you have access to.
+For specific resource types like SQL Virtual Machines and SQL Managed Instances, the script can optionally start the resource if it is in a stopped state (when the -ForceStartOnResources parameter is enabled) before applying the license update.
+
+The script processes several types of Azure SQL resources including:
+
+SQL Virtual Machines (SQL VMs)
+SQL Managed Instances
+SQL Databases
+Elastic Pools
+SQL Instance Pools
+DataFactory SSIS Integration Runtimes
+This automation helps ensure that your licensing configuration is consistent across your environment without manual intervention.
+
+.PARAMETER SubId
+ A single subscription ID or a CSV file name containing a list of subscriptions.
+
+.PARAMETER ResourceGroup
+ Optional. Limit the scope to a specific resource group.
+
+.PARAMETER LicenseType
+ Optional. License type to set. Allowed values: "LicenseIncluded" (default) or "BasePrice".
+
+.PARAMETER ForceStartOnResources
+ Optional. If true, starts SQL VMs and SQL Managed Instances before updating their license type.
+#>
+
+param (
+ [Parameter(Mandatory = $false)]
+ [string] $SubId,
+
+ [Parameter(Mandatory = $false)]
+ [string] $ResourceGroup,
+
+ [Parameter(Mandatory = $false)]
+ [ValidateSet("LicenseIncluded", "BasePrice", IgnoreCase = $false)]
+ [string] $LicenseType = "LicenseIncluded",
+
+ [Parameter(Mandatory = $false)]
+ [bool] $ForceStartOnResources = $false
+)
+
+# Suppress unnecessary logging output
+$VerbosePreference = "SilentlyContinue"
+$DebugPreference = "SilentlyContinue"
+$ProgressPreference = "SilentlyContinue"
+$InformationPreference = "SilentlyContinue"
+$WarningPreference = "SilentlyContinue"
+function Connect-Azure {
+ <#
+ .SYNOPSIS
+ Connects to Azure using either Managed Identity or interactive user login.
+
+ .DESCRIPTION
+ This function first attempts to authenticate to Azure using a Managed Identity by calling
+ Connect-AzAccount with the -Identity switch. If the Managed Identity login is successful,
+ it then connects the Azure CLI using the --identity option. If any of the Managed Identity
+ login attempts fail, the function falls back to interactive (user) login for both Azure
+ PowerShell and the Azure CLI.
+
+ .EXAMPLE
+ PS C:\> Connect-Azure
+ Attempts to connect using Managed Identity; if not available, it will prompt for interactive login.
+ #>
+
+ # Variable to track if Managed Identity authentication was successful.
+ $ManagedIdentityLoginSuccessful = $false
+
+ Write-Output "Attempting to connect using Managed Identity for Azure PowerShell..."
+ try {
+ Connect-AzAccount -Identity -ErrorAction Stop | Out-Null
+ Write-Output "Azure PowerShell connected using Managed Identity."
+ $ManagedIdentityLoginSuccessful = $true
+ }
+ catch {
+ Write-Output "Managed Identity login failed for Azure PowerShell. Falling back to interactive login..."
+ try {
+ Connect-AzAccount -ErrorAction Stop | Out-Null
+ Write-Output "Azure PowerShell connected using interactive login."
+ }
+ catch {
+ Write-Error "Failed to connect to Azure PowerShell: $_"
+ return
+ }
+ }
+
+ # Attempt Azure CLI connection.
+ if ($ManagedIdentityLoginSuccessful) {
+ Write-Output "Attempting to connect Azure CLI using Managed Identity..."
+ try {
+ az login --identity --output none
+ Write-Output "Azure CLI connected using Managed Identity."
+ }
+ catch {
+ Write-Output "Managed Identity login failed for Azure CLI. Falling back to interactive login..."
+ try {
+ az login --output none
+ Write-Output "Azure CLI connected using interactive login."
+ }
+ catch {
+ Write-Error "Failed to connect to Azure CLI interactively: $_"
+ }
+ }
+ }
+ else {
+ Write-Output "Attempting to connect Azure CLI using interactive login..."
+ try {
+ az login --output none
+ Write-Output "Azure CLI connected using interactive login."
+ }
+ catch {
+ Write-Error "Failed to connect to Azure CLI interactively: $_"
+ }
+ }
+}
+
+# Initialize final status and report counters.
+$finalStatus = @()
+$report = @{
+ "SQLVMUpdated" = @()
+ "SQLMIUpdated" = @()
+ "SQLDBUpdated" = @()
+ "ElasticPoolUpdated" = @()
+ "InstancePoolUpdated" = @()
+ "ADFSSISUpdated" = @()
+}
+
+# Ensure connection with both PowerShell and CLI.
+Connect-Azure
+$context = Get-AzContext -ErrorAction SilentlyContinue
+Write-Output "Connected to Azure as: $($context.Account)"
+
+# Map License Types for SQL VMs: LicenseIncluded -> PAYG, BasePrice -> AHUB.
+$SqlVmLicenseType = if ($LicenseType -eq "LicenseIncluded") { "PAYG" } else { "AHUB" }
+
+# Determine the subscriptions to process: CSV file, single subscription, or all accessible subscriptions.
+try {
+ if ($SubId -and $SubId -like "*.csv") {
+ Write-Output "Gathering subscriptions from CSV file: $SubId"
+ $subscriptions = Import-Csv -Path $SubId
+ }
+ elseif ($SubId) {
+ Write-Output "Gathering subscription details for: $SubId"
+ $subscriptions = @(az account show --subscription $SubId --output json | ConvertFrom-Json)
+ }
+ else {
+ Write-Output "Gathering all accessible subscriptions..."
+ $subscriptions = az account list --output json | ConvertFrom-Json
+ }
+}
+catch {
+ Write-Error "Error determining subscriptions: $_"
+ exit 1
+}
+
+# Build resource group filter if specified.
+$rgFilter = if ($ResourceGroup) { "resourceGroup=='$ResourceGroup'" } else { "" }
+$scriptStartTime = Get-Date
+Write-Output "Our adventure begins at: $scriptStartTime`n"
+
+# Process each subscription.
+foreach ($sub in $subscriptions) {
+ try {
+ Write-Output "===== Entering the realm of Subscription: $($sub.name) ====="
+ Write-Output "Switching context to subscription: $($sub.name)"
+ az account set --subscription $sub.id
+
+ # --- Section: Update SQL Virtual Machines ---
+ try {
+ Write-Output "Seeking SQL Virtual Machines that require a license update..."
+ $sqlVmQuery = if ($rgFilter) {
+ "[?sqlServerLicenseType!='${SqlVmLicenseType}' && $rgFilter]"
+ }
+ else {
+ "[?sqlServerLicenseType!='${SqlVmLicenseType}']"
+ }
+ $sqlVMs = az sql vm list --query $sqlVmQuery -o json | ConvertFrom-Json
+ $sqlVmsToUpdate = [System.Collections.ArrayList]::new()
+
+ foreach ($sqlvm in $sqlVMs) {
+ $vmStatus = az vm get-instance-view --resource-group $sqlvm.resourceGroup --name $sqlvm.name --query "{Name:name, ResourceGroup:resourceGroup, PowerState:instanceView.statuses[?starts_with(code, 'PowerState/')].displayStatus | [0]}" -o json | ConvertFrom-Json
+ if ($vmStatus.PowerState -eq "VM running") {
+ Write-Output "Updating SQL VM '$($sqlvm.name)' in RG '$($sqlvm.resourceGroup)' to license type '$SqlVmLicenseType'..."
+ $result = az sql vm update -n $sqlvm.name -g $sqlvm.resourceGroup --license-type $SqlVmLicenseType -o json | ConvertFrom-Json
+ $finalStatus += $result
+ $report["SQLVMUpdated"] += $sqlvm.name
+ }
+ else {
+ if ($ForceStartOnResources) {
+ Write-Output "SQL VM '$($sqlvm.name)' is not running. Forcing start to update license..."
+ az vm start --resource-group $sqlvm.resourceGroup --name $sqlvm.name --no-wait yes
+ $sqlVmsToUpdate.Add($sqlvm) | Out-Null
+ }
+ }
+ }
+ }
+ catch {
+ Write-Error "An error occurred while updating SQL VMs: $_"
+ }
+
+ # --- Section: Update SQL Managed Instances (Stopped then Ready) ---
+ $sqlMIsToUpdate = [System.Collections.ArrayList]::new()
+ try {
+ if ($ForceStartOnResources) {
+ Write-Output "Seeking SQL Managed Instances that are stopped and require an update..."
+ $miQuery = if ($rgFilter) {
+ "[?licenseType!='${LicenseType}' && state!='Ready' && $rgFilter].{Name:name, State:state, ResourceGroup:resourceGroup}"
+ }
+ else {
+ "[?licenseType!='${LicenseType}' && state!='Ready'].{Name:name, State:state, ResourceGroup:resourceGroup}"
+ }
+ $offSQLMIs = az sql mi list --query $miQuery -o json | ConvertFrom-Json
+ foreach ($mi in $offSQLMIs) {
+ if ($mi.State -eq "Stopped") {
+ Write-Output "Starting SQL Managed Instance '$($mi.Name)' in RG '$($mi.ResourceGroup)'..."
+ az sql mi start --mi $mi.Name -g $mi.ResourceGroup --no-wait yes
+ }
+ $sqlMIsToUpdate.Add($mi) | Out-Null
+ }
+ }
+
+ Write-Output "Processing SQL Managed Instances that are running..."
+ $miRunningQuery = if ($rgFilter) {
+ "[?licenseType!='${LicenseType}' && state=='Ready' && $rgFilter]"
+ }
+ else {
+ "[?licenseType!='${LicenseType}' && state=='Ready']"
+ }
+ $runningMIs = az sql mi list --query $miRunningQuery -o json | ConvertFrom-Json
+ foreach ($mi in $runningMIs) {
+ Write-Output "Updating SQL Managed Instance '$($mi.name)' in RG '$($mi.resourceGroup)' to license type '$LicenseType'..."
+ $result = az sql mi update --name $mi.name --resource-group $mi.resourceGroup --license-type $LicenseType -o json | ConvertFrom-Json
+ $finalStatus += $result
+ $report["SQLMIUpdated"] += $mi.name
+ }
+ }
+ catch {
+ Write-Error "An error occurred while updating SQL Managed Instances: $_"
+ }
+
+ # --- Section: Update SQL Databases and Elastic Pools ---
+ try {
+ Write-Output "Querying SQL Servers within this subscription..."
+ $serverQuery = if ($rgFilter) { "[?$rgFilter]" } else { "[]" }
+ $servers = az sql server list --query $serverQuery -o json | ConvertFrom-Json
+
+ foreach ($server in $servers) {
+ # Update SQL Databases
+ Write-Output "Scanning SQL Databases on server '$($server.name)'..."
+ $dbs = az sql db list --resource-group $server.resourceGroup --server $server.name --query "[?licenseType!='$($LicenseType)' && licenseType!=null]" -o json | ConvertFrom-Json
+ foreach ($db in $dbs) {
+ Write-Output "Updating SQL Database '$($db.name)' on server '$($server.name)' to license type '$LicenseType'..."
+ $result = az sql db update --name $db.name --server $server.name --resource-group $server.resourceGroup --set licenseType=$LicenseType -o json | ConvertFrom-Json
+ $finalStatus += $result
+ $report["SQLDBUpdated"] += $db.name
+ }
+
+ # Update Elastic Pools
+ try {
+ Write-Output "Scanning Elastic Pools on server '$($server.name)'..."
+ $elasticPools = az sql elastic-pool list --resource-group $server.resourceGroup --server $server.name --query "[?licenseType!='$($LicenseType)' && licenseType!=null]" --only-show-errors -o json | ConvertFrom-Json
+ foreach ($pool in $elasticPools) {
+ Write-Output "Updating Elastic Pool '$($pool.name)' on server '$($server.name)' to license type '$LicenseType'..."
+ $result = az sql elastic-pool update --name $pool.name --server $server.name --resource-group $server.resourceGroup --set licenseType=$LicenseType --only-show-errors -o json | ConvertFrom-Json -ErrorAction SilentlyContinue
+ $finalStatus += $result
+ $report["ElasticPoolUpdated"] += $pool.name
+ }
+ }
+ catch {
+ Write-Output "Encountered an issue while updating Elastic Pools on server '$($server.name)'. Continuing..."
+ }
+ }
+ }
+ catch {
+ Write-Error "An error occurred while processing SQL Databases or Elastic Pools: $_"
+ }
+
+ # --- Section: Update SQL Instance Pools ---
+ try {
+ Write-Output "Searching for SQL Instance Pools that require a license update..."
+ $instancePoolsQuery = if ($rgFilter) { "[?$rgFilter]" } else { "[]" }
+ $instancePools = az sql instance-pool list --query $instancePoolsQuery -o json | ConvertFrom-Json
+ $poolsToUpdate = $instancePools | Where-Object { $_.licenseType -ne $LicenseType }
+ foreach ($pool in $poolsToUpdate) {
+ Write-Output "Updating SQL Instance Pool '$($pool.name)' in RG '$($pool.resourceGroup)' to license type '$LicenseType'..."
+ $result = az sql instance-pool update --name $pool.name --resource-group $pool.resourceGroup --license-type $LicenseType -o json | ConvertFrom-Json
+ $finalStatus += $result
+ $report["InstancePoolUpdated"] += $pool.name
+ }
+ }
+ catch {
+ Write-Error "An error occurred while updating SQL Instance Pools: $_"
+ }
+
+ # --- Section: Update DataFactory SSIS Integration Runtimes ---
+ try {
+ Write-Output "Processing DataFactory SSIS Integration Runtime resources..."
+ Get-AzDataFactoryV2 | Where-Object { $_.ProvisioningState -eq "Succeeded" } | ForEach-Object {
+ Get-AzDataFactoryV2IntegrationRuntime -ResourceGroupName $_.ResourceGroupName -DataFactoryName $_.DataFactoryName | Where-Object { $_.Type -eq "Managed" -and $_.State -ne "Starting" } | ForEach-Object {
+ if ($_.LicenseType -ne $LicenseType) {
+ # Update the license type to $LicenseType.
+ $result = Set-AzDataFactoryV2IntegrationRuntime -ResourceGroupName $_.ResourceGroupName -DataFactoryName $_.DataFactoryName -Name $_.Name -LicenseType $LicenseType -Force
+ $finalStatus += $result
+ $report["ADFSSISUpdated"] += $_.Name
+ Write-Host ([Environment]::NewLine + "-- DataFactory '$($_.DataFactoryName)' integration runtime updated to license type $LicenseType")
+ }
+ }
+ }
+ }
+ catch {
+ Write-Error "An error occurred while updating DataFactory SSIS Integration Runtimes: $_"
+ }
+
+ # --- Section: Finalize SQL VM updates for those that were started on-demand ---
+ $sqlvm = ""
+ try {
+ $updated = $true
+ while ($sqlVmsToUpdate.Count -gt 0) {
+ $sqlvm = $sqlVmsToUpdate[0]
+ $vmStatus = az vm get-instance-view --resource-group $sqlvm.resourceGroup --name $sqlvm.name --query "{PowerState:instanceView.statuses[?starts_with(code, 'PowerState/')].displayStatus | [0]}" -o json | ConvertFrom-Json
+ if ($vmStatus.PowerState -eq "VM running") {
+ Write-Output "Now updating SQL VM '$($sqlvm.name)' after forced start..."
+ $result = az sql vm update -n $sqlvm.name -g $sqlvm.resourceGroup --license-type $SqlVmLicenseType -o json | ConvertFrom-Json
+ $finalStatus += $result
+ $report["SQLVMUpdated"] += $sqlvm.name
+ Write-Output "Deallocating SQL VM '$($sqlvm.name)' post-update..."
+ az vm deallocate --resource-group $sqlvm.resourceGroup --name $sqlvm.name --no-wait yes
+ $sqlVmsToUpdate.RemoveAt(0)
+ $updated = $true
+ }
+ else {
+ if ($updated) {
+ Write-Host "Waiting for SQL VM '$($sqlvm.name)' to start..."
+ $updated = $false
+ }
+ else {
+ Write-Host "." -NoNewline
+ }
+ Start-Sleep -Seconds 30
+ }
+ }
+ }
+ catch {
+ Write-Error "An error occurred while finalizing SQL VM updates in subscription '$($sub.name)': $sqlvm"
+ }
+
+ # --- Section: Finalize SQL Managed Instance updates for those that were forced-start ---
+ $mi = ""
+ try {
+ $updated = $true
+ while ($sqlMIsToUpdate.Count -gt 0) {
+ $mi = $sqlMIsToUpdate[0]
+ $miStatus = az sql mi show --resource-group $mi.ResourceGroup --name $mi.Name -o json | ConvertFrom-Json
+ if ($miStatus.state -eq "Ready") {
+ Write-Output "Updating SQL Managed Instance '$($mi.Name)' after forced start..."
+ $result = az sql mi update --name $mi.Name --resource-group $mi.ResourceGroup --license-type $LicenseType -o json | ConvertFrom-Json
+ $finalStatus += $result
+ $report["SQLMIUpdated"] += $mi.Name
+ Write-Output "Stopping SQL Managed Instance '$($mi.Name)' post-update..."
+ az sql mi stop --resource-group $mi.ResourceGroup --mi $mi.Name --no-wait yes
+ $sqlMIsToUpdate.RemoveAt(0)
+ $updated = $true
+ }
+ else {
+ if ($updated) {
+ Write-Host "Waiting for SQL Managed Instance '$($mi.Name)' to be ready..."
+ $updated = $false
+ }
+ else {
+ Write-Host "." -NoNewline
+ }
+ Start-Sleep -Seconds 30
+ }
+ }
+ }
+ catch {
+ Write-Error "An error occurred while finalizing SQL Managed Instance updates in subscription '$($sub.name)': $mi"
+ }
+
+ }
+ catch {
+ Write-Error "An error occurred while processing subscription '$($sub.name)': $_"
+ }
+}
+
+$scriptEndTime = Get-Date
+$totalDuration = $scriptEndTime - $scriptStartTime
+
+# --- Final Report ---
+Write-Output "`n===== Final Report ====="
+Write-Output "Script started at: $scriptStartTime"
+Write-Output "Script ended at: $scriptEndTime"
+Write-Output "Total duration: $($totalDuration.ToString())"
+Write-Output "`nResources updated by category:"
+
+if ($report["SQLVMUpdated"].Count -gt 0) {
+ Write-Output "SQL VMs Updated: $($report["SQLVMUpdated"] -join ', ')"
+} else {
+ Write-Output "SQL VMs Updated: None"
+}
+
+if ($report["SQLMIUpdated"].Count -gt 0) {
+ Write-Output "SQL Managed Instances Updated: $($report["SQLMIUpdated"] -join ', ')"
+} else {
+ Write-Output "SQL Managed Instances Updated: None"
+}
+
+if ($report["SQLDBUpdated"].Count -gt 0) {
+ Write-Output "SQL Databases Updated: $($report["SQLDBUpdated"] -join ', ')"
+} else {
+ Write-Output "SQL Databases Updated: None"
+}
+
+if ($report["ElasticPoolUpdated"].Count -gt 0) {
+ Write-Output "Elastic Pools Updated: $($report["ElasticPoolUpdated"] -join ', ')"
+} else {
+ Write-Output "Elastic Pools Updated: None"
+}
+
+if ($report["InstancePoolUpdated"].Count -gt 0) {
+ Write-Output "SQL Instance Pools Updated: $($report["InstancePoolUpdated"] -join ', ')"
+} else {
+ Write-Output "SQL Instance Pools Updated: None"
+}
+
+if ($report["ADFSSISUpdated"].Count -gt 0) {
+ Write-Output "SQL ADF SSIS: $($report["ADFSSISUpdated"] -join ', ')"
+} else {
+ Write-Output "SQL ADF SSIS Updated: None"
+}
\ No newline at end of file
diff --git a/samples/manage/azure-sql-db-managed-instance/attach-vpn-gateway/attachVPNGateway.ps1 b/samples/manage/azure-sql-db-managed-instance/attach-vpn-gateway/attachVPNGateway.ps1
index aa66caa0ec..cc5aff0de9 100644
--- a/samples/manage/azure-sql-db-managed-instance/attach-vpn-gateway/attachVPNGateway.ps1
+++ b/samples/manage/azure-sql-db-managed-instance/attach-vpn-gateway/attachVPNGateway.ps1
@@ -163,6 +163,15 @@ function CalculateNextAddressPrefix {
}
}
$startIPAddress += 1
+ # if crossing a block boundary, round to the next possible start for the given suffix size
+ $suffixLength = 32 - $prefixLength
+ $mask = (1 -shl $suffixLength) - 1
+ if (($startIPAddress -band $mask) -ne $startIPAddress) {
+ $x = $startIPAddress -shr $suffixLength
+ $x += 1
+ $startIPAddress = $x -shl $suffixLength
+ }
+ # convert and return
$addressPrefixResult = (ConvertUInt32ToIPAddress $startIPAddress) + "/" + $prefixLength
Write-Host "Using address prefix $addressPrefixResult." -ForegroundColor Green
return $addressPrefixResult
@@ -239,7 +248,7 @@ $gatewaySubnetName = "GatewaySubnet"
If ($false -eq $subnets.Contains($gatewaySubnetName)) {
Write-Host "$gatewaySubnetName is not one of the subnets in $subnets" -ForegroundColor Yellow
- $gatewaySubnetPrefix = CalculateNextAddressPrefix $virtualNetwork 28
+ $gatewaySubnetPrefix = CalculateNextAddressPrefix $virtualNetwork 27
Write-Host "Creating subnet $gatewaySubnetName ($gatewaySubnetPrefix) in the virtual network ..." -ForegroundColor Green
$virtualNetwork.AddressSpace.AddressPrefixes.Add($gatewaySubnetPrefix)
diff --git a/samples/manage/azure-sql-db-managed-instance/attach-vpn-gateway/azuredeploy.json b/samples/manage/azure-sql-db-managed-instance/attach-vpn-gateway/azuredeploy.json
index fa4e4dd0b2..ea933aa994 100644
--- a/samples/manage/azure-sql-db-managed-instance/attach-vpn-gateway/azuredeploy.json
+++ b/samples/manage/azure-sql-db-managed-instance/attach-vpn-gateway/azuredeploy.json
@@ -46,8 +46,12 @@
"type": "Microsoft.Network/publicIPAddresses",
"name": "[variables('gatewayPublicIpAddressName')]",
"location": "[parameters('location')]",
+ "sku": {
+ "name": "Standard"
+ },
"properties": {
- "publicIPAllocationMethod": "Dynamic"
+ "publicIPAllocationMethod": "Static",
+ "publicIPAddressVersion": "IPv4"
}
},
{
@@ -74,8 +78,8 @@
}
],
"sku": {
- "name": "Standard",
- "tier": "Standard"
+ "name": "VpnGw1AZ",
+ "tier": "VpnGw1AZ"
},
"gatewayType": "Vpn",
"vpnType": "RouteBased",
diff --git a/samples/manage/azure-sql-db-managed-instance/delegate-subnet/delegateSubnet.ps1 b/samples/manage/azure-sql-db-managed-instance/delegate-subnet/delegateSubnet.ps1
index e3010da16a..529a35307a 100644
--- a/samples/manage/azure-sql-db-managed-instance/delegate-subnet/delegateSubnet.ps1
+++ b/samples/manage/azure-sql-db-managed-instance/delegate-subnet/delegateSubnet.ps1
@@ -17,7 +17,7 @@ function VerifyPSVersion {
Write-Host "PowerShell version verified." -ForegroundColor Green
}
else {
- Write-Host "You need to install PowerShell version 5.1 or heigher." -ForegroundColor Red
+ Write-Host "You need to install PowerShell version 5.1 or higher." -ForegroundColor Red
Break;
}
}
@@ -26,7 +26,7 @@ function VerifyPSVersion {
Write-Host "PowerShell version verified." -ForegroundColor Green
}
else {
- Write-Host "You need to install PowerShell version 6.0 or heigher." -ForegroundColor Red
+ Write-Host "You need to install PowerShell version 6.0 or higher." -ForegroundColor Red
Break;
}
}
diff --git a/samples/manage/polybase/external-table/README.md b/samples/manage/polybase/external-table/README.md
new file mode 100644
index 0000000000..670b1c228d
--- /dev/null
+++ b/samples/manage/polybase/external-table/README.md
@@ -0,0 +1,113 @@
+
+
+
+# Handling UPDATE STATISTICS on SQL Server PolyBase external tables
+
+This sample describes an option to update statistics on SQL Server PolyBase external tables.
+
+## Background
+
+In the process of configuring a maintenance plan for a SQL Server database with external tables and external data sources for PolyBase queries, an error occurred during the update statistics task.
+
+## Problem encountered
+
+While attempting to update statistics on an external table, the following error message was encountered:
+
+```sql
+UPDATE STATISTICS [dbo].[ExternalTableName] WITH FULLSCAN, COLUMNS
+
+Message 46519, level 16, state 22
+
+The object Update Statistics isn't supported on External Table
+```
+
+### Contents
+
+[About this sample](#about-this-sample)
+[Before you begin](#before-you-begin)
+[Case history](#case-history)
+[Resolution steps](#resolution-steps)
+[Further considerations](#further-considerations)
+[Outcome](#outcome)
+[Disclaimers](#disclaimers)
+[Related links](#related-links)
+
+
+
+## About this sample
+
+- **Applies to:** SQL Server 2017 (or higher)
+- **Key features:** UPDATE STATISTICS
+- **Workload:** No workload related to this sample
+- **Programming Language:** T-SQL
+- **Authors:** [Sergio Govoni](https://www.linkedin.com/in/sgovoni/) | [Microsoft MVP Profile](https://mvp.microsoft.com/mvp/profile/c7b770c0-3c9a-e411-93f2-9cb65495d3c4) | [Blog](https://segovoni.medium.com/) | [GitHub](https://github.com/segovoni) | [Twitter](https://twitter.com/segovoni)
+
+
+
+## Before you begin
+
+To run this example, the following basic concepts are required.
+
+[SQL Server PolyBase](https://learn.microsoft.com/sql/relational-databases/polybase/polybase-guide?WT.mc_id=DP-MVP-4029181) enables your SQL Server instance to query data with T-SQL directly from SQL Server, Oracle, Teradata, MongoDB, Hadoop clusters, Cosmos DB, and S3-compatible object storage without separately installing client connection software. If you are new to PolyBase, you can find initial information in this article: [Polybase for beginners](https://techcommunity.microsoft.com/t5/sql-server-support-blog/polybase-for-beginners/ba-p/1075336).
+
+
+
+## Case history
+
+Upon investigation, it became apparent that SQL Server does not support the direct updating of statistics on external tables, as documented in the [CREATE STATISTICS documentation page](https://learn.microsoft.com/sql/t-sql/statements/create-statistics-transact-sql?WT.mc_id=DP-MVP-4029181#limitations-and-restrictions).
+
+
+
+## Resolution steps
+
+### Understanding the limitation
+
+Referring to the documentation page, it explicitly states, "Updating statistics is not supported on external tables. To update statistics on an external table, drop and re-create the statistics."
+
+### Available solutions
+
+* Option 1: Ignore External Tables:
+ * This option is not feasible for maintenance plans managed by SQL Server Management Studio, necessitating third-party solutions.
+
+* Option 2: Drop and Recreate Statistics:
+ * Employ a stored procedure, [sp_drop_create_stats_external_table](https://github.com/microsoft/sql-server-samples/tree/master/samples/manage/polybase/external-table/source/sp-drop-create-stats-external-table.sql), to generate T-SQL statements for dropping and creating statistics on external tables. This procedure supports statistics on multiple columns.
+
+### Implementation guide
+
+- Execute `sp_drop_create_stats_external_table` to generate T-SQL statements
+- Execute DROP STATISTICS statements before the maintenance statistics task
+- Execute CREATE STATISTICS statements after the maintenance statistics task
+- Implement robust error handling during the maintenance plan execution
+- Validate the presence of statistics on the external table upon completion
+
+
+
+## Further considerations
+
+- When dealing with large external tables, consider the performance implications of using default sampling versus full scan options
+- Note that external tables utilizing DELIMITEDTEXT, CSV, PARQUET, or DELTA as data types only support statistics for one column per CREATE STATISTICS command
+
+
+
+## Outcome
+
+By integrating the `sp_drop_create_stats_external_table` stored procedure into the maintenance plan, developers can effectively manage statistics on external tables within SQL Server databases, ensuring optimal performance and reliability.
+
+From the maintenance plan prospective, CREATE and DROP STATISTICS statements can be stored on a temporary table or working table and executed separately. DROP STATISTICS statements can be executed before the maintenance statistics task and afterward the CREATE STATISTICS statements. Pay attention to the error handling during the maintenance plan because, at the end of the maintenance, statistics have to be in place on the external table. The output of the stored procedure cannot be missed.
+
+
+
+## Disclaimers
+
+This code sample is provided for demonstration and educational purposes only. It is recommended to use it with caution and fully understand its implications before applying it in a production environment. The provided code may not fully reflect the best development or security practices and may require adjustments to meet specific project requirements. The author disclaims any liability for any damages resulting from the use or interpretation of this material.
+
+
+
+## Related links
+
+
+For more information, see these articles:
+
+- [Data virtualization with PolyBase in SQL Server](https://learn.microsoft.com/sql/relational-databases/polybase/polybase-guide?WT.mc_id=DP-MVP-4029181)
+- [UPDATE STATISTICS (Transact-SQL)](https://learn.microsoft.com/sql/t-sql/statements/update-statistics-transact-sql?WT.mc_id=DP-MVP-4029181)
+- [External tables: Why create statistics?](https://learn.microsoft.com/answers/questions/978155/external-tables-why-create-statistics?WT.mc_id=DP-MVP-4029181)
diff --git a/samples/manage/polybase/external-table/source/sp-drop-create-stats-external-table.sql b/samples/manage/polybase/external-table/source/sp-drop-create-stats-external-table.sql
new file mode 100644
index 0000000000..d937266f52
--- /dev/null
+++ b/samples/manage/polybase/external-table/source/sp-drop-create-stats-external-table.sql
@@ -0,0 +1,213 @@
+------------------------------------------------------------------------
+-- Project: sp_drop_create_stats_external_table --
+-- --
+-- The stored procedure is able to generate all the --
+-- T-SQL statements for drop and create statistics --
+-- defined on SQL Server external tables in your --
+-- database! --
+-- --
+-- File: Stored procedure implementation --
+-- Author: Sergio Govoni https://www.linkedin.com/in/sgovoni/ --
+-- Notes: -- --
+------------------------------------------------------------------------
+
+
+IF OBJECT_ID('dbo.sp_drop_create_stats_external_table', 'P') IS NOT NULL
+ DROP PROCEDURE dbo.sp_drop_create_stats_external_table;
+GO
+
+CREATE PROCEDURE dbo.sp_drop_create_stats_external_table
+AS BEGIN
+ /*
+ Author: Sergio Govoni https://www.linkedin.com/in/sgovoni/
+ Version: 1.0
+ License: MIT License
+ */
+
+ DECLARE
+ -- Output table
+ @DropCreateSQLCmd TABLE
+ (
+ ID INTEGER IDENTITY(1, 1) NOT NULL
+ ,SchemaName SYSNAME NOT NULL
+ ,TableName SYSNAME NOT NULL
+ ,ObjectType SYSNAME NOT NULL
+ ,OperationType NCHAR(1) NOT NULL
+ ,SQLCmd NVARCHAR(1024) NOT NULL
+ );
+
+ -- Generate CREATE STATISTICS statements
+ WITH StatCreate AS
+ (
+ SELECT
+ 'A' AS RowType
+ ,T.[object_id]
+ ,T.stats_id
+ ,T.StatLevel
+ ,T.KeyOrdinal
+ ,T.SchemaName
+ ,T.TableName
+ ,CAST('CREATE ' +
+ 'STATISTICS [' + TRIM(T.StatsName) +
+ '] ON [' + TRIM(T.SchemaName) +
+ '].[' + TRIM(T.TableName) +
+ '] ( ' AS VARCHAR(MAX)) AS SQLCmd
+ FROM
+ (
+ SELECT
+ DISTINCT
+ stat.[object_id]
+ ,stat.stats_id
+ ,CAST(0 AS INTEGER) AS StatLevel
+ ,CAST(0 AS INTEGER) AS KeyOrdinal
+ ,stat.name AS StatsName
+ ,sch.name AS SchemaName
+ ,obj.name AS TableName
+ FROM
+ sys.stats_columns AS statc
+ JOIN
+ sys.stats AS stat ON ((stat.stats_id = statc.stats_id)
+ AND (stat.[object_id] = statc.[object_id]))
+ JOIN
+ sys.objects AS obj ON statc.[object_id] = obj.[object_id]
+ JOIN
+ sys.external_tables external_tab ON ((external_tab.[object_id] = obj.[object_id])
+ AND (external_tab.[schema_id] = obj.[schema_id]))
+ JOIN
+ sys.columns AS col ON ((col.column_id = statc.column_id)
+ AND (col.[object_id] = statc.[object_id]))
+ JOIN
+ sys.schemas AS sch ON obj.[schema_id] = sch.[schema_id]
+ WHERE
+ ((stat.auto_created = 1) OR (stat.user_created = 1))
+ AND (obj.type = 'U')
+ ) AS T
+
+ UNION ALL
+
+ SELECT
+ 'R' AS RowType
+ ,statcol.[object_id]
+ ,statcol.stats_id
+ ,CAST(S.StatLevel + 1 AS INTEGER) AS IdxLevel
+ ,CAST(statcol.stats_column_id AS INTEGER) AS KeyOrdinal
+ ,S.SchemaName
+ ,S.TableName
+ ,CAST(S.SQLCmd + CASE(statcol.stats_column_id) WHEN 1 THEN '' ELSE ',' END +
+ '[' + TRIM(col.name) +
+ '] ' AS VARCHAR(MAX)) AS SQLCmd
+ FROM
+ StatCreate AS S
+ JOIN
+ sys.stats_columns AS statcol ON ((statcol.[object_id] = S.[object_id])
+ AND (statcol.stats_id = S.stats_id))
+ JOIN
+ sys.columns AS col ON ((col.column_id = statcol.column_id)
+ AND (col.[object_id] = statcol.[object_id]))
+ WHERE
+ (statcol.stats_column_id = (S.KeyOrdinal + 1))
+ ),
+ StatCreateGroup AS
+ (
+ SELECT
+ MAX(S.KeyOrdinal) AS MaxKeyOrdinal
+ ,S.[object_id]
+ ,S.stats_id
+ FROM
+ StatCreate AS S
+ JOIN
+ sys.objects AS O ON O.[object_id] = S.[object_id]
+ WHERE
+ (S.RowType = 'R')
+ GROUP BY
+ S.[object_id]
+ ,S.stats_id
+ )
+ INSERT INTO @DropCreateSQLCmd
+ (
+ SchemaName
+ ,TableName
+ ,ObjectType
+ ,OperationType
+ ,SQLCmd
+ )
+ SELECT
+ StatCreate.SchemaName
+ ,StatCreate.TableName
+ ,'STATS' AS ObjecType
+ ,'C' AS OperationType
+ ,StatCreate.SQLCmd + ') WITH FULLSCAN;' AS SQLCmd
+ FROM
+ StatCreateGroup
+ JOIN
+ StatCreate ON ((StatCreate.[object_id] = StatCreateGroup.[object_id])
+ AND (StatCreate.stats_id = StatCreateGroup.stats_id))
+ AND (StatCreate.KeyOrdinal = StatCreateGroup.MaxKeyOrdinal);
+
+ -- Generate DROP STATISTICS statements
+ WITH StatsDrop AS
+ (
+ SELECT
+ T.SchemaName
+ ,T.TableName
+ ,'STATS' AS ObjectType
+ ,'D' AS OperationType
+ ,'DROP STATISTICS [' +
+ TRIM(SchemaName) + '].[' +
+ TRIM(TableName) + '].[' +
+ TRIM(StatisticName) + '];' AS SQLCmd
+
+ FROM (
+ SELECT
+ sch.[Name] as SchemaName
+ ,obj.[Name] as TableName
+ ,stat.[Name] as StatisticName
+ FROM
+ sys.stats AS stat
+ INNER JOIN
+ sys.objects AS obj ON stat.[object_id] = obj.[object_id]
+ INNER JOIN
+ sys.external_tables external_tab ON ((external_tab.[object_id] = obj.[object_id])
+ AND (external_tab.[schema_id] = obj.[schema_id]))
+ INNER JOIN
+ sys.schemas AS sch ON obj.[schema_id] = sch.[schema_id]
+ WHERE
+ ((stat.auto_created = 1) OR (stat.user_created = 1))
+ AND (obj.type = 'U')
+ ) AS T
+ )
+ INSERT INTO @DropCreateSQLCmd
+ (
+ SchemaName
+ ,TableName
+ ,ObjectType
+ ,OperationType
+ ,SQLCmd
+ )
+ SELECT
+ SchemaName
+ ,TableName
+ ,ObjectType
+ ,OperationType
+ ,SQLCmd
+ FROM
+ StatsDrop;
+
+ SELECT
+ ID
+ ,SchemaName
+ ,TableName
+ ,ObjectType
+ ,OperationType
+ ,SQLCmd
+ FROM
+ @DropCreateSQLCmd;
+
+ RETURN;
+END;
+GO
+
+/*
+EXEC dbo.sp_drop_create_stats_external_table;
+GO
+*/
\ No newline at end of file
diff --git a/samples/manage/sql-vm-io-analysis.ps1 b/samples/manage/sql-vm-io-analysis.ps1
new file mode 100644
index 0000000000..9f0195255a
--- /dev/null
+++ b/samples/manage/sql-vm-io-analysis.ps1
@@ -0,0 +1,186 @@
+# Enter parameters
+$subscriptionId = Read-Host ""
+$resourceGroup = Read-Host ""
+$vmName = Read-Host ""
+
+# Set resource details
+$resourceType = "Microsoft.Compute/virtualMachines"
+$resourceId = "/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/$resourceType/$vmName"
+
+# Get Azure access token
+$accessToken = az account get-access-token --query accessToken -o tsv
+
+# Invoke Azure Monitor Metrics API
+function Get-Metrics {
+ [CmdletBinding()]
+ param (
+ [string]$accessToken,
+ [string]$resourceId,
+ [string]$metricNames,
+ [string]$apiVersion = "2023-10-01"
+ )
+ try {
+ $startTime = (Get-Date).AddHours(-24).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')
+ $endTime = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')
+ $timespan = "$startTime/$endTime"
+ Write-Verbose "Evaluating timespan: $timespan"
+ $uri = "https://management.azure.com$resourceId/providers/Microsoft.Insights/metrics?api-version=$apiVersion&metricnames=$metricNames&aggregation=maximum&interval=PT1M×pan=$timespan"
+ $headers = @{ "Authorization" = "Bearer $accessToken"; "Content-Type" = "application/json" }
+
+ $response = Invoke-RestMethod -Uri $uri -Headers $headers -Method Get
+ if ($response) {
+ Write-Verbose "API response successfully retrieved."
+ return $response
+ } else {
+ Write-Error "No response from API."
+ }
+ } catch {
+ Write-Error "Error retrieving metrics: $_"
+ }
+}
+
+# Check if data disk latency violates thresholds
+function Check-Latency {
+ [CmdletBinding()]
+ param (
+ [Parameter(Mandatory = $true)]
+ [Object]$metrics,
+
+ [Parameter()]
+ [int]$latencyThreshold = 500,
+
+ [Parameter()]
+ [int]$consecutiveCount = 5
+ )
+ $violationTimes = @()
+ foreach ($metric in $metrics.value) {
+ if ($metric.name.value -eq "Data Disk Latency") {
+ $count = 0
+ foreach ($dataPoint in $metric.timeseries[0].data) {
+ if ($dataPoint.maximum -gt $latencyThreshold) {
+ $count++
+ if ($count -ge $consecutiveCount) {
+ $violationTimes += $dataPoint.timeStamp
+ $count = 0 # Reset count after recording a violation
+ }
+ } else {
+ $count = 0 # Reset count if the sequence is broken
+ }
+ }
+ }
+ }
+ if ($violationTimes.Count -gt 0) {
+ Write-Verbose "Latency violations detected."
+ return @{ "Flag" = $true; "Times" = $violationTimes }
+ } else {
+ Write-Verbose "No latency violations detected."
+ return @{ "Flag" = $false }
+ }
+}
+
+# Check metrics other than latency to evaluate for throttling
+function Check-OtherMetricsThrottled {
+ [CmdletBinding()]
+ param (
+ [Parameter(Mandatory = $true)]
+ [Object]$metrics,
+
+ [Parameter()]
+ [int]$PercentageThreshold = 90,
+
+ [Parameter()]
+ [int]$consecutiveCount = 5
+ )
+ $violatedMetrics = @()
+ foreach ($metric in $metrics.value) {
+ $count = 0
+ foreach ($dataPoint in $metric.timeseries[0].data) {
+ if ($dataPoint.maximum -gt $PercentageThreshold) {
+ $count++
+ if ($count -ge $consecutiveCount) {
+ $violatedMetrics += @{ "Metric" = $metric.name.localizedValue; "Time" = $dataPoint.timeStamp; "Value" = $dataPoint.maximum }
+ break
+ }
+ } else {
+ $count = 0
+ }
+ }
+ }
+ if ($violatedMetrics.Count -gt 0) {
+ Write-Verbose "Other metrics violations detected."
+ } else {
+ Write-Verbose "No other metrics violations detected."
+ }
+ return $violatedMetrics
+}
+
+# Compare times for latency & other throttled metrics. Logs the volations with values & timestamps
+function CompareTimes {
+ [CmdletBinding()]
+ param (
+ [Parameter(Mandatory = $true)]
+ [Hashtable]$latencyResult,
+
+ [Parameter(Mandatory = $true)]
+ [Array]$otherMetrics
+ )
+ foreach ($metric in $otherMetrics) {
+ $otherDateTime = [DateTime]$metric["Time"]
+ $isWithinFiveMinutes = $false
+ $closestLatencyTime = $null
+ $closestTimeDifference = [int]::MaxValue
+
+ foreach ($latencyTime in $latencyResult.Times) {
+ $latencyDateTime = [DateTime]$latencyTime
+ $timeDifference = [Math]::Abs(($otherDateTime - $latencyDateTime).TotalMinutes)
+
+ if ($timeDifference -le 5) {
+ $isWithinFiveMinutes = $true
+ if ($timeDifference -lt $closestTimeDifference) {
+ $closestTimeDifference = $timeDifference
+ $closestLatencyTime = $latencyTime
+ }
+ }
+ }
+
+ if ($isWithinFiveMinutes) {
+ if ($otherDateTime -lt $closestLatencyTime) {
+ Write-Host "`n $($metric["Metric"]) limit was hit before latency spiked at $closestLatencyTime with value $($metric["Value"]). `n"
+ } else {
+ Write-Host "`n $($metric["Metric"]) hit its limit with value $($metric["Value"]) at $($metric["Time"])."
+ Write-Host "Latency spiked at $closestLatencyTime before $($metric["Metric"]) hit its limit `n"
+ }
+ } else {
+ Write-Host "`n Metric: $($metric["Metric"]) exceeded its threshold with a value of $($metric["Value"]) at $($metric["Time"]), but this was not within 5 minutes of any latency spikes."
+ }
+ }
+}
+
+# Prompt user for latency threshold
+$latencyThreshold = Read-Host "Enter Latency Threshold (default is 500)"
+if (-not [int]::TryParse($latencyThreshold, [ref]0)) {
+ $latencyThreshold = 500 # Use default if invalid input
+ Write-Host "No valid input provided. Using Default 500ms for disk latency threshold"
+}
+
+# Execute main logic
+$latencyMetrics = Get-Metrics -accessToken $accessToken -resourceId $resourceId -metricNames "Data Disk Latency"
+$latencyResult = Check-Latency -metrics $latencyMetrics -latencyThreshold $latencyThreshold
+
+if ($latencyResult.Flag) {
+
+ # If latency is flagged, check for other metrics. If there is no disk latency, machine is likely not throttled but only at high consumption
+ Write-Verbose "Checking the following metrics: Data Disk Bandwidth Consumed Percentage,Data Disk IOPS Consumed Percentage,VM Cached Bandwidth Consumed Percentage,VM Cached IOPS Consumed Percentage,VM Uncached Bandwidth Consumed Percentage,VM Uncached IOPS Consumed Percentage"
+
+ $DiskVMMetrics = Get-Metrics -accessToken $accessToken -resourceId $resourceId -metricNames "Data Disk Bandwidth Consumed Percentage,Data Disk IOPS Consumed Percentage,VM Cached Bandwidth Consumed Percentage,VM Cached IOPS Consumed Percentage,VM Uncached Bandwidth Consumed Percentage,VM Uncached IOPS Consumed Percentage"
+
+ $additionalMetrics = Check-OtherMetricsThrottled -metrics $DiskVMMetrics
+
+ if ($additionalMetrics.Count -gt 0) {
+ CompareTimes $latencyResult $additionalMetrics
+ } else {
+ Write-Host "No metrics violations detected besides latency."
+ }
+} else {
+ Write-Host "No latency issues detected."
+}
diff --git a/samples/tutorials/AzureSqlGettingStartedSamples/java/Unix-based/AzureSqlHibernateSample/pom.xml b/samples/tutorials/AzureSqlGettingStartedSamples/java/Unix-based/AzureSqlHibernateSample/pom.xml
index 563c34af82..ac8bec2711 100644
--- a/samples/tutorials/AzureSqlGettingStartedSamples/java/Unix-based/AzureSqlHibernateSample/pom.xml
+++ b/samples/tutorials/AzureSqlGettingStartedSamples/java/Unix-based/AzureSqlHibernateSample/pom.xml
@@ -50,7 +50,7 @@
com.azure
azure-identity
- 1.0.4
+ 1.12.2
org.slf4j
diff --git a/samples/tutorials/AzureSqlGettingStartedSamples/java/Unix-based/AzureSqlSample/pom.xml b/samples/tutorials/AzureSqlGettingStartedSamples/java/Unix-based/AzureSqlSample/pom.xml
index 38e3a77e96..cbf298da03 100644
--- a/samples/tutorials/AzureSqlGettingStartedSamples/java/Unix-based/AzureSqlSample/pom.xml
+++ b/samples/tutorials/AzureSqlGettingStartedSamples/java/Unix-based/AzureSqlSample/pom.xml
@@ -42,7 +42,7 @@
com.azure
azure-identity
- 1.0.4
+ 1.12.2
org.slf4j
diff --git a/sql-server-samples b/sql-server-samples
new file mode 160000
index 0000000000..bafb8e95e3
--- /dev/null
+++ b/sql-server-samples
@@ -0,0 +1 @@
+Subproject commit bafb8e95e3fcb17e635fab377e3bf2e8e68e78c4