Additional Telemetry - Implementation of RFC0036
(#10336)
This commit is contained in:
parent
c8e72d1e66
commit
fe2cc6aca8
@ -1012,7 +1012,7 @@ function Start-PSPester {
|
||||
# All concatenated commands/arguments are suffixed with the delimiter (space)
|
||||
|
||||
# Disable telemetry for all startups of pwsh in tests
|
||||
$command = "`$env:POWERSHELL_TELEMETRY_OPTOUT = 1;"
|
||||
$command = "`$env:POWERSHELL_TELEMETRY_OPTOUT = 'yes';"
|
||||
if ($Terse)
|
||||
{
|
||||
$command += "`$ProgressPreference = 'silentlyContinue'; "
|
||||
@ -1153,7 +1153,7 @@ function Start-PSPester {
|
||||
try {
|
||||
$originalModulePath = $env:PSModulePath
|
||||
$originalTelemetry = $env:POWERSHELL_TELEMETRY_OPTOUT
|
||||
$env:POWERSHELL_TELEMETRY_OPTOUT = 1
|
||||
$env:POWERSHELL_TELEMETRY_OPTOUT = 'yes'
|
||||
if ($Unelevate)
|
||||
{
|
||||
Start-UnelevatedProcess -process $powershell -arguments ($PSFlags + "-c $Command")
|
||||
|
@ -8,8 +8,6 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\System.Management.Automation\System.Management.Automation.csproj" />
|
||||
<!-- the following package(s) are from https://github.com/microsoft/applicationinsights-??? -->
|
||||
<PackageReference Include="Microsoft.ApplicationInsights" Version="2.10.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
@ -24,6 +24,7 @@ using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.PowerShell.Telemetry;
|
||||
|
||||
using ConsoleHandle = Microsoft.Win32.SafeHandles.SafeFileHandle;
|
||||
using Dbg = System.Management.Automation.Diagnostics;
|
||||
@ -123,12 +124,8 @@ namespace Microsoft.PowerShell
|
||||
|
||||
try
|
||||
{
|
||||
string profileDir;
|
||||
#if UNIX
|
||||
profileDir = Platform.SelectProductNameForDirectory(Platform.XDG_Type.CACHE);
|
||||
#else
|
||||
profileDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"\Microsoft\PowerShell";
|
||||
|
||||
string profileDir = Platform.CacheDirectory;
|
||||
#if ! UNIX
|
||||
if (!Directory.Exists(profileDir))
|
||||
{
|
||||
Directory.CreateDirectory(profileDir);
|
||||
@ -200,12 +197,14 @@ namespace Microsoft.PowerShell
|
||||
// First check for and handle PowerShell running in a server mode.
|
||||
if (s_cpp.ServerMode)
|
||||
{
|
||||
ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("ServerMode");
|
||||
ProfileOptimization.StartProfile("StartupProfileData-ServerMode");
|
||||
System.Management.Automation.Remoting.Server.OutOfProcessMediator.Run(s_cpp.InitialCommand);
|
||||
exitCode = 0;
|
||||
}
|
||||
else if (s_cpp.NamedPipeServerMode)
|
||||
{
|
||||
ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("NamedPipe");
|
||||
ProfileOptimization.StartProfile("StartupProfileData-NamedPipeServerMode");
|
||||
System.Management.Automation.Remoting.RemoteSessionNamedPipeServer.RunServerMode(
|
||||
s_cpp.ConfigurationName);
|
||||
@ -213,12 +212,14 @@ namespace Microsoft.PowerShell
|
||||
}
|
||||
else if (s_cpp.SSHServerMode)
|
||||
{
|
||||
ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("SSHServer");
|
||||
ProfileOptimization.StartProfile("StartupProfileData-SSHServerMode");
|
||||
System.Management.Automation.Remoting.Server.SSHProcessMediator.Run(s_cpp.InitialCommand);
|
||||
exitCode = 0;
|
||||
}
|
||||
else if (s_cpp.SocketServerMode)
|
||||
{
|
||||
ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("SocketServerMode");
|
||||
ProfileOptimization.StartProfile("StartupProfileData-SocketServerMode");
|
||||
System.Management.Automation.Remoting.Server.HyperVSocketMediator.Run(s_cpp.InitialCommand,
|
||||
s_cpp.ConfigurationName);
|
||||
@ -242,7 +243,7 @@ namespace Microsoft.PowerShell
|
||||
PSHost.IsStdOutputRedirected = Console.IsOutputRedirected;
|
||||
|
||||
// Send startup telemetry for ConsoleHost startup
|
||||
ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry();
|
||||
ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("Normal");
|
||||
|
||||
exitCode = s_theConsoleHost.Run(s_cpp, false);
|
||||
}
|
||||
|
@ -1,102 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Management.Automation;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
using Microsoft.ApplicationInsights;
|
||||
using Microsoft.ApplicationInsights.DataContracts;
|
||||
using Microsoft.ApplicationInsights.Extensibility;
|
||||
|
||||
namespace Microsoft.PowerShell
|
||||
{
|
||||
/// <summary>
|
||||
/// Send up telemetry for startup.
|
||||
/// </summary>
|
||||
internal static class ApplicationInsightsTelemetry
|
||||
{
|
||||
// If this env var is true, yes, or 1, telemetry will NOT be sent.
|
||||
private const string TelemetryOptoutEnvVar = "POWERSHELL_TELEMETRY_OPTOUT";
|
||||
|
||||
// Telemetry client to be reused when we start sending more telemetry
|
||||
private static TelemetryClient _telemetryClient = null;
|
||||
|
||||
// Set this to true to reduce the latency of sending the telemetry
|
||||
private static bool _developerMode = false;
|
||||
|
||||
// PSCoreInsight2 telemetry key
|
||||
private const string _psCoreTelemetryKey = "ee4b2115-d347-47b0-adb6-b19c2c763808";
|
||||
|
||||
static ApplicationInsightsTelemetry()
|
||||
{
|
||||
TelemetryConfiguration.Active.InstrumentationKey = _psCoreTelemetryKey;
|
||||
TelemetryConfiguration.Active.TelemetryChannel.DeveloperMode = _developerMode;
|
||||
}
|
||||
|
||||
private static bool GetEnvironmentVariableAsBool(string name, bool defaultValue)
|
||||
{
|
||||
var str = Environment.GetEnvironmentVariable(name);
|
||||
if (string.IsNullOrEmpty(str))
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
switch (str.ToLowerInvariant())
|
||||
{
|
||||
case "true":
|
||||
case "1":
|
||||
case "yes":
|
||||
return true;
|
||||
case "false":
|
||||
case "0":
|
||||
case "no":
|
||||
return false;
|
||||
default:
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send the telemetry.
|
||||
/// </summary>
|
||||
private static void SendTelemetry(string eventName, Dictionary<string, string> payload)
|
||||
{
|
||||
try
|
||||
{
|
||||
var enabled = !GetEnvironmentVariableAsBool(name: TelemetryOptoutEnvVar, defaultValue: false);
|
||||
|
||||
if (!enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_telemetryClient == null)
|
||||
{
|
||||
_telemetryClient = new TelemetryClient();
|
||||
}
|
||||
|
||||
_telemetryClient.TrackEvent(eventName, payload, null);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
; // Do nothing, telemetry can't be sent
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the startup payload and send it up.
|
||||
/// </summary>
|
||||
internal static void SendPSCoreStartupTelemetry()
|
||||
{
|
||||
var properties = new Dictionary<string, string>();
|
||||
properties.Add("GitCommitID", PSVersionInfo.GitCommitId);
|
||||
properties.Add("OSDescription", RuntimeInformation.OSDescription);
|
||||
SendTelemetry("ConsoleHostStartup", properties);
|
||||
}
|
||||
}
|
||||
}
|
@ -140,6 +140,21 @@ namespace System.Management.Automation
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the location for the various caches.
|
||||
/// </summary>
|
||||
internal static string CacheDirectory
|
||||
{
|
||||
get
|
||||
{
|
||||
#if UNIX
|
||||
return Platform.SelectProductNameForDirectory(Platform.XDG_Type.CACHE);
|
||||
#else
|
||||
return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"\Microsoft\PowerShell";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if !UNIX
|
||||
private static bool? _isNanoServer = null;
|
||||
private static bool? _isIoT = null;
|
||||
|
@ -13,6 +13,8 @@
|
||||
<ItemGroup>
|
||||
<!-- the following package(s) are from https://github.com/JamesNK/Newtonsoft.Json -->
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||
<!-- the Application Insights package -->
|
||||
<PackageReference Include="Microsoft.ApplicationInsights" Version="2.10.0" />
|
||||
<!-- the following package(s) are from https://github.com/dotnet/corefx -->
|
||||
<PackageReference Include="Microsoft.Win32.Registry.AccessControl" Version="4.6.0-preview8.19405.3" />
|
||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="4.6.0-preview8.19405.3" />
|
||||
|
@ -515,9 +515,12 @@ namespace System.Management.Automation
|
||||
// This is the start of the real implementation of autocomplete/intellisense/tab completion
|
||||
private static CommandCompletion CompleteInputImpl(Ast ast, Token[] tokens, IScriptPosition positionOfCursor, Hashtable options)
|
||||
{
|
||||
#if LEGACYTELEMETRY
|
||||
// We could start collecting telemetry at a later date.
|
||||
// We will leave the #if to remind us that we did this once.
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
|
||||
#endif
|
||||
using (var powershell = PowerShell.Create(RunspaceMode.CurrentRunspace))
|
||||
{
|
||||
var context = LocalPipeline.GetExecutionContextFromTLS();
|
||||
@ -590,8 +593,10 @@ namespace System.Management.Automation
|
||||
}
|
||||
|
||||
var completionResults = results ?? EmptyCompletionResult;
|
||||
sw.Stop();
|
||||
|
||||
#if LEGACYTELEMETRY
|
||||
// no telemetry here. We don't capture tab completion performance.
|
||||
sw.Stop();
|
||||
TelemetryAPI.ReportTabCompletionTelemetry(sw.ElapsedMilliseconds, completionResults.Count,
|
||||
completionResults.Count > 0 ? completionResults[0].ResultType : CompletionResultType.Text);
|
||||
#endif
|
||||
|
@ -9,6 +9,7 @@ using System.Management.Automation.Configuration;
|
||||
using System.Management.Automation.Internal;
|
||||
using System.Management.Automation.Tracing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Microsoft.PowerShell.Telemetry;
|
||||
|
||||
namespace System.Management.Automation
|
||||
{
|
||||
@ -150,6 +151,7 @@ namespace System.Management.Automation
|
||||
if (IsModuleFeatureName(name))
|
||||
{
|
||||
list.Add(name);
|
||||
ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ExperimentalModuleFeatureActivation, name);
|
||||
}
|
||||
else if (IsEngineFeatureName(name))
|
||||
{
|
||||
@ -157,6 +159,7 @@ namespace System.Management.Automation
|
||||
{
|
||||
feature.Enabled = true;
|
||||
list.Add(name);
|
||||
ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ExperimentalEngineFeatureActivation, name);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -369,8 +369,9 @@ namespace Microsoft.PowerShell.Commands
|
||||
/// </summary>
|
||||
protected override void BeginProcessing()
|
||||
{
|
||||
#if LEGACYTELEMETRY
|
||||
_timer.Start();
|
||||
|
||||
#endif
|
||||
base.BeginProcessing();
|
||||
|
||||
if (ShowCommandInfo.IsPresent && Syntax.IsPresent)
|
||||
@ -552,9 +553,11 @@ namespace Microsoft.PowerShell.Commands
|
||||
}
|
||||
}
|
||||
|
||||
#if LEGACYTELEMETRY
|
||||
_timer.Stop();
|
||||
|
||||
#if LEGACYTELEMETRY
|
||||
// No telemetry here - capturing the name of a command which we are not familiar with
|
||||
// may be confidential customer information
|
||||
// We want telementry on commands people look for but don't exist - this should give us an idea
|
||||
// what sort of commands people expect but either don't exist, or maybe should be installed by default.
|
||||
// The StartsWith is to avoid logging telemetry when suggestion mode checks the
|
||||
@ -1443,8 +1446,9 @@ namespace Microsoft.PowerShell.Commands
|
||||
private Collection<WildcardPattern> _nounPatterns;
|
||||
private Collection<WildcardPattern> _modulePatterns;
|
||||
|
||||
#if LEGACYTELEMETRY
|
||||
private Stopwatch _timer = new Stopwatch();
|
||||
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region ShowCommandInfo support
|
||||
|
@ -10,6 +10,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Management.Automation;
|
||||
using System.Management.Automation.Internal;
|
||||
using System.Management.Automation.Language;
|
||||
using System.Management.Automation.Runspaces;
|
||||
using System.Reflection;
|
||||
using System.Security;
|
||||
@ -17,13 +18,13 @@ using System.Threading;
|
||||
|
||||
using Microsoft.Management.Infrastructure;
|
||||
using Microsoft.PowerShell.Cmdletization;
|
||||
using Microsoft.PowerShell.Telemetry;
|
||||
|
||||
using Dbg = System.Management.Automation.Diagnostics;
|
||||
|
||||
using System.Management.Automation.Language;
|
||||
using Parser = System.Management.Automation.Language.Parser;
|
||||
using ScriptBlock = System.Management.Automation.ScriptBlock;
|
||||
using Token = System.Management.Automation.Language.Token;
|
||||
|
||||
#if LEGACYTELEMETRY
|
||||
using Microsoft.PowerShell.Telemetry.Internal;
|
||||
#endif
|
||||
@ -825,6 +826,12 @@ namespace Microsoft.PowerShell.Commands
|
||||
}
|
||||
}
|
||||
|
||||
// Send telemetry on the imported modules
|
||||
foreach (PSModuleInfo moduleInfo in remotelyImportedModules)
|
||||
{
|
||||
ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ModuleLoad, moduleInfo.Name);
|
||||
}
|
||||
|
||||
return remotelyImportedModules;
|
||||
}
|
||||
|
||||
@ -1251,6 +1258,7 @@ namespace Microsoft.PowerShell.Commands
|
||||
foreach (RemoteDiscoveryHelper.CimModule remoteCimModule in remotePsCimModules)
|
||||
{
|
||||
ImportModule_RemotelyViaCimModuleData(importModuleOptions, remoteCimModule, cimSession);
|
||||
ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ModuleLoad, remoteCimModule.ModuleName);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1743,6 +1751,7 @@ namespace Microsoft.PowerShell.Commands
|
||||
// of doing Get-Module -list
|
||||
foreach (PSModuleInfo module in ModuleInfo)
|
||||
{
|
||||
ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ModuleLoad, module.Name);
|
||||
RemoteDiscoveryHelper.DispatchModuleInfoProcessing(
|
||||
module,
|
||||
localAction: delegate ()
|
||||
@ -1772,6 +1781,7 @@ namespace Microsoft.PowerShell.Commands
|
||||
{
|
||||
foreach (Assembly suppliedAssembly in Assembly)
|
||||
{
|
||||
ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ModuleLoad, suppliedAssembly.GetName().Name);
|
||||
ImportModule_ViaAssembly(importModuleOptions, suppliedAssembly);
|
||||
}
|
||||
}
|
||||
@ -1785,6 +1795,8 @@ namespace Microsoft.PowerShell.Commands
|
||||
{
|
||||
SetModuleBaseForEngineModules(foundModule.Name, this.Context);
|
||||
|
||||
// Telemetry here - report module load
|
||||
ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ModuleLoad, foundModule.Name);
|
||||
#if LEGACYTELEMETRY
|
||||
TelemetryAPI.ReportModuleLoad(foundModule);
|
||||
#endif
|
||||
@ -1809,6 +1821,7 @@ namespace Microsoft.PowerShell.Commands
|
||||
BaseGuid = modulespec.Guid;
|
||||
|
||||
PSModuleInfo foundModule = ImportModule_LocallyViaName(importModuleOptions, modulespec.Name);
|
||||
ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ModuleLoad, modulespec.Name);
|
||||
if (foundModule != null)
|
||||
{
|
||||
SetModuleBaseForEngineModules(foundModule.Name, this.Context);
|
||||
@ -1818,6 +1831,10 @@ namespace Microsoft.PowerShell.Commands
|
||||
else if (this.ParameterSetName.Equals(ParameterSet_FQName_ViaPsrpSession, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
ImportModule_RemotelyViaPsrpSession(importModuleOptions, null, FullyQualifiedName, this.PSSession);
|
||||
foreach (ModuleSpecification modulespec in FullyQualifiedName)
|
||||
{
|
||||
ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ModuleLoad, modulespec.Name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
@ -16,6 +17,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.Management.Infrastructure;
|
||||
using Microsoft.PowerShell.Telemetry;
|
||||
|
||||
using Dbg = System.Management.Automation.Diagnostics;
|
||||
|
||||
@ -638,6 +640,7 @@ namespace System.Management.Automation
|
||||
Streams = new PSDataStreams(this);
|
||||
_endInvokeMethod = EndInvoke;
|
||||
_endStopMethod = EndStop;
|
||||
ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.PowerShellCreate, "create");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -7,6 +7,7 @@ using System.Management.Automation.Runspaces;
|
||||
using System.Management.Automation.Tracing;
|
||||
using System.Reflection;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using Microsoft.PowerShell.Telemetry;
|
||||
|
||||
using Dbg = System.Management.Automation.Diagnostics;
|
||||
|
||||
@ -1025,6 +1026,10 @@ namespace System.Management.Automation.Internal
|
||||
CommandState.Started,
|
||||
commandProcessor.Command.MyInvocation);
|
||||
|
||||
// Telemetry here
|
||||
// the type of command should be sent along
|
||||
// commandProcessor.CommandInfo.CommandType
|
||||
ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ApplicationType, commandProcessor.Command.CommandInfo.CommandType.ToString());
|
||||
#if LEGACYTELEMETRY
|
||||
Microsoft.PowerShell.Telemetry.Internal.TelemetryAPI.TraceExecutedCommand(commandProcessor.Command.CommandInfo, commandProcessor.Command.CommandOrigin);
|
||||
#endif
|
||||
|
@ -10,6 +10,7 @@ using System.Management.Automation.Remoting;
|
||||
using System.Management.Automation.Remoting.Client;
|
||||
using System.Management.Automation.Tracing;
|
||||
using System.Threading;
|
||||
using Microsoft.PowerShell.Telemetry;
|
||||
|
||||
using Dbg = System.Management.Automation.Diagnostics;
|
||||
#if LEGACYTELEMETRY
|
||||
@ -854,6 +855,8 @@ namespace System.Management.Automation.Runspaces.Internal
|
||||
PSEtwLog.LogOperationalVerbose(PSEventId.RunspacePoolOpen, PSOpcode.Open,
|
||||
PSTask.CreateRunspace, PSKeyword.UseAlwaysOperational);
|
||||
|
||||
// Telemetry here - remote session
|
||||
ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.RemoteSessionOpen, isAsync.ToString());
|
||||
#if LEGACYTELEMETRY
|
||||
TelemetryAPI.ReportRemoteSessionCreated(_connectionInfo);
|
||||
#endif
|
||||
|
397
src/System.Management.Automation/utils/Telemetry.cs
Normal file
397
src/System.Management.Automation/utils/Telemetry.cs
Normal file
@ -0,0 +1,397 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Management.Automation;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
using Microsoft.ApplicationInsights;
|
||||
using Microsoft.ApplicationInsights.Extensibility;
|
||||
|
||||
namespace Microsoft.PowerShell.Telemetry
|
||||
{
|
||||
/// <summary>
|
||||
/// The category of telemetry.
|
||||
/// </summary>
|
||||
internal enum TelemetryType
|
||||
{
|
||||
/// <summary>
|
||||
/// Telemetry of the application type (cmdlet, script, etc).
|
||||
/// </summary>
|
||||
ApplicationType,
|
||||
|
||||
/// <summary>
|
||||
/// Send telemetry when we load a module, only module names in the s_knownModules list
|
||||
/// will be reported, otherwise it will be "anonymous".
|
||||
/// </summary>
|
||||
ModuleLoad,
|
||||
|
||||
/// <summary>
|
||||
/// Send telemetry for experimental module feature activation.
|
||||
/// All experimental engine features will be have telemetry.
|
||||
/// </summary>
|
||||
ExperimentalEngineFeatureActivation,
|
||||
|
||||
/// <summary>
|
||||
/// Send telemetry for experimental module feature activation.
|
||||
/// Experimental module features will send telemetry based on the module it is in.
|
||||
/// If we send telemetry for the module, we will also do so for any experimental feature
|
||||
/// in that module.
|
||||
/// </summary>
|
||||
ExperimentalModuleFeatureActivation,
|
||||
|
||||
/// <summary>
|
||||
/// Send telemetry for each PowerShell.Create API.
|
||||
/// </summary>
|
||||
PowerShellCreate,
|
||||
|
||||
/// <summary>
|
||||
/// Remote session creation.
|
||||
/// </summary>
|
||||
RemoteSessionOpen,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send up telemetry for startup.
|
||||
/// </summary>
|
||||
public static class ApplicationInsightsTelemetry
|
||||
{
|
||||
// If this env var is true, yes, or 1, telemetry will NOT be sent.
|
||||
private const string _telemetryOptoutEnvVar = "POWERSHELL_TELEMETRY_OPTOUT";
|
||||
|
||||
// PSCoreInsight2 telemetry key
|
||||
// private const string _psCoreTelemetryKey = "ee4b2115-d347-47b0-adb6-b19c2c763808"; // Production
|
||||
private const string _psCoreTelemetryKey = "d26a5ef4-d608-452c-a6b8-a4a55935f70d"; // V7 Preview 3
|
||||
|
||||
// Use "anonymous" as the string to return when you can't report a name
|
||||
private const string _anonymous = "anonymous";
|
||||
|
||||
// the telemetry failure string
|
||||
private const string _telemetryFailure = "TELEMETRY_FAILURE";
|
||||
|
||||
// Telemetry client to be reused when we start sending more telemetry
|
||||
private static TelemetryClient s_telemetryClient { get; set; }
|
||||
|
||||
// the unique identifier for the user, when we start we
|
||||
private static string s_uniqueUserIdentifier { get; set; }
|
||||
|
||||
// the session identifier
|
||||
private static string s_sessionId { get; set; }
|
||||
|
||||
/// Use a hashset for quick lookups.
|
||||
/// We send telemetry only a known set of modules.
|
||||
/// If it's not in the list (initialized in the static constructor), then we report anonymous.
|
||||
private static HashSet<string> s_knownModules;
|
||||
|
||||
/// <summary>Gets a value indicating whether telemetry can be sent.</summary>
|
||||
public static bool CanSendTelemetry { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes static members of the <see cref="ApplicationInsightsTelemetry"/> class.
|
||||
/// Static constructor determines whether telemetry is to be sent, and then
|
||||
/// sets the telemetry key and set the telemetry delivery mode.
|
||||
/// Creates the session ID and initializes the HashSet of known module names.
|
||||
/// Gets or constructs the unique identifier.
|
||||
/// </summary>
|
||||
static ApplicationInsightsTelemetry()
|
||||
{
|
||||
// If we can't send telemetry, there's no reason to do any of this
|
||||
CanSendTelemetry = !GetEnvironmentVariableAsBool(name: _telemetryOptoutEnvVar, defaultValue: false);
|
||||
if (CanSendTelemetry)
|
||||
{
|
||||
s_telemetryClient = new TelemetryClient();
|
||||
TelemetryConfiguration.Active.InstrumentationKey = _psCoreTelemetryKey;
|
||||
// Set this to true to reduce latency during development
|
||||
TelemetryConfiguration.Active.TelemetryChannel.DeveloperMode = false;
|
||||
s_sessionId = Guid.NewGuid().ToString();
|
||||
|
||||
// use a hashset when looking for module names, it should be quicker than a string comparison
|
||||
s_knownModules = new HashSet<string>(StringComparer.OrdinalIgnoreCase) {
|
||||
"Microsoft.PowerShell.Archive",
|
||||
"Microsoft.PowerShell.Host",
|
||||
"Microsoft.PowerShell.Management",
|
||||
"Microsoft.PowerShell.Security",
|
||||
"Microsoft.PowerShell.Utility",
|
||||
"PackageManagement",
|
||||
"Pester",
|
||||
"PowerShellGet",
|
||||
"PSDesiredStateConfiguration",
|
||||
"PSReadLine",
|
||||
"ThreadJob",
|
||||
};
|
||||
s_uniqueUserIdentifier = GetUniqueIdentifier().ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine whether the environment variable is set and how.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the environment variable.</param>
|
||||
/// <param name="defaultValue">If the environment variable is not set, use this as the default value.</param>
|
||||
/// <returns>A boolean representing the value of the environment variable.</returns>
|
||||
private static bool GetEnvironmentVariableAsBool(string name, bool defaultValue)
|
||||
{
|
||||
var str = Environment.GetEnvironmentVariable(name);
|
||||
if (string.IsNullOrEmpty(str))
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
switch (str.ToLowerInvariant())
|
||||
{
|
||||
case "true":
|
||||
case "1":
|
||||
case "yes":
|
||||
return true;
|
||||
case "false":
|
||||
case "0":
|
||||
case "no":
|
||||
return false;
|
||||
default:
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send telemetry as a metric.
|
||||
/// </summary>
|
||||
/// <param name="metricId">The type of telemetry that we'll be sending.</param>
|
||||
/// <param name="data">The specific details about the telemetry.</param>
|
||||
internal static void SendTelemetryMetric(TelemetryType metricId, string data)
|
||||
{
|
||||
if (!CanSendTelemetry)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string metricName = metricId.ToString();
|
||||
try
|
||||
{
|
||||
switch (metricId)
|
||||
{
|
||||
case TelemetryType.ApplicationType:
|
||||
case TelemetryType.PowerShellCreate:
|
||||
case TelemetryType.RemoteSessionOpen:
|
||||
case TelemetryType.ExperimentalEngineFeatureActivation:
|
||||
s_telemetryClient.GetMetric(metricName, "uuid", "SessionId", "Detail").TrackValue(metricValue: 1.0, s_uniqueUserIdentifier, s_sessionId, data);
|
||||
break;
|
||||
case TelemetryType.ExperimentalModuleFeatureActivation:
|
||||
string experimentalFeatureName = GetExperimentalFeatureName(data);
|
||||
s_telemetryClient.GetMetric(metricName, "uuid", "SessionId", "Detail").TrackValue(metricValue: 1.0, s_uniqueUserIdentifier, s_sessionId, experimentalFeatureName);
|
||||
break;
|
||||
case TelemetryType.ModuleLoad:
|
||||
string moduleName = GetModuleName(data); // This will return anonymous if the modulename is not on the report list
|
||||
s_telemetryClient.GetMetric(metricName, "uuid", "SessionId", "Detail").TrackValue(metricValue: 1.0, s_uniqueUserIdentifier, s_sessionId, moduleName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// do nothing, telemetry can't be sent
|
||||
// don't send the panic telemetry as if we have failed above, it will likely fail here.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Get the experimental feature name. If we can report it, we'll return the name of the feature, otherwise, we'll return "anonymous"
|
||||
private static string GetExperimentalFeatureName(string featureNameToValidate)
|
||||
{
|
||||
// An experimental feature in a module is guaranteed to start with the module name
|
||||
// we can strip out the text past the last '.' as the text before that will be the ModuleName
|
||||
int lastDotIndex = featureNameToValidate.LastIndexOf('.');
|
||||
string moduleName = featureNameToValidate.Substring(0, lastDotIndex);
|
||||
if (s_knownModules.Contains(moduleName))
|
||||
{
|
||||
return featureNameToValidate;
|
||||
}
|
||||
|
||||
return _anonymous;
|
||||
}
|
||||
|
||||
// Get the module name. If we can report it, we'll return the name, otherwise, we'll return "anonymous"
|
||||
private static string GetModuleName(string moduleNameToValidate)
|
||||
{
|
||||
if (s_knownModules.Contains(moduleNameToValidate))
|
||||
{
|
||||
return moduleNameToValidate;
|
||||
}
|
||||
|
||||
return _anonymous;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the startup payload and send it up.
|
||||
/// This is done only once during for the console host.
|
||||
/// </summary>
|
||||
/// <param name="mode">The "mode" of the startup.</param>
|
||||
internal static void SendPSCoreStartupTelemetry(string mode)
|
||||
{
|
||||
if (!CanSendTelemetry)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var properties = new Dictionary<string, string>();
|
||||
// The variable POWERSHELL_DISTRIBUTION_CHANNEL is set in our docker images.
|
||||
// This allows us to track the actual docker OS as OSDescription provides only "linuxkit"
|
||||
// which has limited usefulness
|
||||
var channel = Environment.GetEnvironmentVariable("POWERSHELL_DISTRIBUTION_CHANNEL");
|
||||
|
||||
properties.Add("SessionId", s_sessionId);
|
||||
properties.Add("UUID", s_uniqueUserIdentifier);
|
||||
properties.Add("GitCommitID", PSVersionInfo.GitCommitId);
|
||||
properties.Add("OSDescription", RuntimeInformation.OSDescription);
|
||||
properties.Add("OSChannel", string.IsNullOrEmpty(channel) ? "unknown" : channel);
|
||||
properties.Add("StartMode", string.IsNullOrEmpty(mode) ? "unknown" : mode);
|
||||
try
|
||||
{
|
||||
s_telemetryClient.TrackEvent("ConsoleHostStartup", properties, null);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// do nothing, telemetry cannot be sent
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to read the file and collect the guid.
|
||||
/// </summary>
|
||||
/// <param name="telemetryFilePath">The path to the telemetry file.</param>
|
||||
/// <param name="id">The newly created id.</param>
|
||||
/// <returns>
|
||||
/// The method returns a bool indicating success or failure of creating the id.
|
||||
/// </returns>
|
||||
private static bool TryGetIdentifier(string telemetryFilePath, out Guid id)
|
||||
{
|
||||
if (File.Exists(telemetryFilePath))
|
||||
{
|
||||
// attempt to read the persisted identifier
|
||||
const int GuidSize = 16;
|
||||
byte[] buffer = new byte[GuidSize];
|
||||
try
|
||||
{
|
||||
using (FileStream fs = new FileStream(telemetryFilePath, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
// if the read is invalid, or wrong size, we return it
|
||||
int n = fs.Read(buffer, 0, GuidSize);
|
||||
if (n == GuidSize)
|
||||
{
|
||||
// it's possible this could through
|
||||
id = new Guid(buffer);
|
||||
if (id != Guid.Empty)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// something went wrong, the file may not exist or not have enough bytes, so return false
|
||||
}
|
||||
}
|
||||
|
||||
id = Guid.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to create a unique identifier and persist it to the telemetry.uuid file.
|
||||
/// </summary>
|
||||
/// <param name="telemetryFilePath">The path to the persisted telemetry.uuid file.</param>
|
||||
/// <param name="id">The created identifier.</param>
|
||||
/// <returns>
|
||||
/// The method returns a bool indicating success or failure of creating the id.
|
||||
/// </returns>
|
||||
private static bool TryCreateUniqueIdentifierAndFile(string telemetryFilePath, out Guid id)
|
||||
{
|
||||
// one last attempt to retrieve before creating incase we have a lot of simultaneous entry into the mutex.
|
||||
id = Guid.Empty;
|
||||
if (TryGetIdentifier(telemetryFilePath, out id))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// The directory may not exist, so attempt to create it
|
||||
// CreateDirectory will simply return the directory if exists
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(telemetryFilePath));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// send a telemetry indicating a problem with the cache dir
|
||||
// it's likely something is seriously wrong so we should at least report it.
|
||||
// We don't want to provide reasons here, that's not the point, but we
|
||||
// would like to know if we're having a generalized problem which we can trace statistically
|
||||
CanSendTelemetry = false;
|
||||
s_telemetryClient.GetMetric(_telemetryFailure, "Detail").TrackValue(1, "cachedir");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create and save the new identifier, and if there's a problem, disable telemetry
|
||||
try
|
||||
{
|
||||
id = Guid.NewGuid();
|
||||
File.WriteAllBytes(telemetryFilePath, id.ToByteArray());
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// another bit of telemetry to notify us about a problem with saving the unique id.
|
||||
s_telemetryClient.GetMetric(_telemetryFailure, "Detail").TrackValue(1, "saveuuid");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve the unique identifier from the persisted file, if it doesn't exist create it.
|
||||
/// Generate a guid which will be used as the UUID.
|
||||
/// </summary>
|
||||
/// <returns>A guid which represents the unique identifier.</returns>
|
||||
private static Guid GetUniqueIdentifier()
|
||||
{
|
||||
// Try to get the unique id. If this returns false, we'll
|
||||
// create/recreate the telemetry.uuid file to persist for next startup.
|
||||
Guid id = Guid.Empty;
|
||||
string uuidPath = Path.Join(Platform.CacheDirectory, "telemetry.uuid");
|
||||
if (TryGetIdentifier(uuidPath, out id))
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
// Multiple processes may start simultaneously so we need a system wide
|
||||
// way to control access to the file in the case (although remote) when we have
|
||||
// simulataneous shell starts without the persisted file which attempt to create the file.
|
||||
using (var m = new Mutex(true, "CreateUniqueUserId"))
|
||||
{
|
||||
// TryCreateUniqueIdentifierAndFile shouldn't throw, but the mutex might
|
||||
try
|
||||
{
|
||||
m.WaitOne();
|
||||
if (TryCreateUniqueIdentifierAndFile(uuidPath, out id))
|
||||
{
|
||||
return id;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Any problem in generating a uuid will result in no telemetry being sent.
|
||||
// Try to send the failure in telemetry, but it will have no unique id.
|
||||
s_telemetryClient.GetMetric(_telemetryFailure, "Detail").TrackValue(1, "mutex");
|
||||
}
|
||||
finally
|
||||
{
|
||||
m.ReleaseMutex();
|
||||
}
|
||||
}
|
||||
|
||||
// something bad happened, turn off telemetry since the unique id wasn't set.
|
||||
CanSendTelemetry = false;
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
@ -95,6 +95,11 @@ Describe "Verify Markdown Links" {
|
||||
it "<url> should work" -TestCases $trueFailures {
|
||||
param($url)
|
||||
|
||||
# there could be multiple reasons why a failure is ok
|
||||
# check against the allowed failures
|
||||
# 503 = service temporarily unavailable
|
||||
$allowedFailures = @( 503 )
|
||||
|
||||
$prefix = $url.Substring(0,7)
|
||||
|
||||
# Logging for diagnosability. Azure DevOps sometimes redacts the full url.
|
||||
@ -108,7 +113,9 @@ Describe "Verify Markdown Links" {
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw "retry of URL failed with error: $($_.Message)"
|
||||
if ( $allowedFailures -notcontains $_.Exception.Response.StatusCode ) {
|
||||
throw "retry of URL failed with error: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -43,7 +43,6 @@ Describe "Validate start of console host" -Tag CI {
|
||||
'System.Private.CoreLib.dll'
|
||||
'System.Private.Uri.dll'
|
||||
'System.Private.Xml.dll'
|
||||
'System.Private.Xml.Linq.dll'
|
||||
'System.Reflection.Emit.ILGeneration.dll'
|
||||
'System.Reflection.Emit.Lightweight.dll'
|
||||
'System.Reflection.Primitives.dll'
|
||||
@ -67,9 +66,7 @@ Describe "Validate start of console host" -Tag CI {
|
||||
'System.Threading.Tasks.Parallel.dll'
|
||||
'System.Threading.Thread.dll'
|
||||
'System.Threading.ThreadPool.dll'
|
||||
'System.Threading.Timer.dll'
|
||||
'System.Xml.ReaderWriter.dll'
|
||||
'System.Xml.XDocument.dll'
|
||||
)
|
||||
|
||||
if ($IsWindows) {
|
||||
|
131
test/powershell/engine/Basic/Telemetry.Tests.ps1
Normal file
131
test/powershell/engine/Basic/Telemetry.Tests.ps1
Normal file
@ -0,0 +1,131 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# unit tests for telemetry
|
||||
# these tests aren't going to check that telemetry is being sent
|
||||
# only that we're not treating the telemetry.uuid file correctly
|
||||
|
||||
Describe "Telemetry for shell startup" -Tag CI {
|
||||
BeforeAll {
|
||||
# if the telemetry file exists, move it out of the way
|
||||
# the member is internal, but we can retrieve it via reflection
|
||||
$cacheDir = [System.Management.Automation.Platform].GetMember("CacheDirectory","NonPublic,Static").GetMethod.Invoke($null, $null)
|
||||
$uuidPath = Join-Path -Path $cacheDir -ChildPath telemetry.uuid
|
||||
$uuidFileExists = Test-Path -Path $uuidPath
|
||||
if ( $uuidFileExists ) {
|
||||
$originalBytes = Get-Content -AsByteStream -Path $uuidPath
|
||||
Rename-Item -Path $uuidPath -NewName "${uuidPath}.original"
|
||||
}
|
||||
|
||||
$PWSH = (Get-Process -Id $PID).MainModule.FileName
|
||||
$telemetrySet = Test-Path -Path env:POWERSHELL_TELEMETRY_OPTOUT
|
||||
$SendingTelemetry = $env:POWERSHELL_TELEMETRY_OPTOUT
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
# check and reset the telemetry.uuid file
|
||||
if ( $uuidFileExists ) {
|
||||
if ( Test-Path -Path "${uuidPath}.original" ) {
|
||||
Rename-Item -NewName $uuidPath -Path "${uuidPath}.original" -Force
|
||||
}
|
||||
else {
|
||||
[System.IO.File]::WriteAllBytes($uuidPath, $originalBytes)
|
||||
}
|
||||
}
|
||||
if ( $telemetrySet ) {
|
||||
$env:POWERSHELL_TELEMETRY_OPTOUT = $SendingTelemetry
|
||||
}
|
||||
}
|
||||
|
||||
AfterEach {
|
||||
if ( Test-Path -Path $uuidPath ) {
|
||||
Remove-Item -Path $uuidPath
|
||||
}
|
||||
if ( Test-Path -Path env:POWERSHELL_TELEMETRY_OPTOUT ) {
|
||||
Remove-Item env:POWERSHELL_TELEMETRY_OPTOUT
|
||||
}
|
||||
}
|
||||
|
||||
It "Should not create a uuid file if telemetry is opted out" {
|
||||
$env:POWERSHELL_TELEMETRY_OPTOUT = 1
|
||||
& $PWSH -NoProfile -Command "exit"
|
||||
$uuidPath | Should -Not -Exist
|
||||
}
|
||||
|
||||
It "Should create a uuid file if telemetry is opted in" {
|
||||
$env:POWERSHELL_TELEMETRY_OPTOUT = "no"
|
||||
& $PWSH -NoProfile -Command "exit"
|
||||
$uuidPath | Should -Exist
|
||||
}
|
||||
|
||||
It "Should create a uuid file by default" {
|
||||
if ( Test-Path env:POWERSHELL_TELEMETRY_OPTOUT ) { Remove-Item -Path env:POWERSHELL_TELEMETRY_OPTOUT }
|
||||
& $PWSH -NoProfile -Command "exit"
|
||||
$uuidPath | Should -Exist
|
||||
}
|
||||
|
||||
It "Should create a property uuid file when telemetry is sent" {
|
||||
$env:POWERSHELL_TELEMETRY_OPTOUT = "no"
|
||||
& $PWSH -NoProfile -Command "exit"
|
||||
$uuidPath | Should -Exist
|
||||
(Get-ChildItem -Path $uuidPath).Length | Should -Be 16
|
||||
[byte[]]$newBytes = Get-Content -AsByteStream -Path $uuidPath
|
||||
[System.Guid]::New($newBytes) | Should -BeOfType [System.Guid]
|
||||
}
|
||||
|
||||
It "Should not create a telemetry file if one already exists and telemetry is opted in" {
|
||||
[byte[]]$bytes = [System.Guid]::NewGuid().ToByteArray()
|
||||
[System.IO.File]::WriteAllBytes($uuidPath, $bytes)
|
||||
& $PWSH -NoProfile -Command "exit"
|
||||
[byte[]]$newBytes = Get-Content -AsByteStream -Path $uuidPath
|
||||
Compare-Object -ReferenceObject $bytes -DifferenceObject $newBytes | Should -BeNullOrEmpty
|
||||
}
|
||||
|
||||
It "Should create a new telemetry file if the current one is 00000000-0000-0000-0000-000000000000" {
|
||||
[byte[]]$zeroGuid = [System.Guid]::Empty.ToByteArray()
|
||||
[System.IO.File]::WriteAllBytes($uuidPath, $zeroGuid)
|
||||
& $PWSH -NoProfile -Command "exit"
|
||||
[byte[]]$newBytes = Get-Content -AsByteStream -Path $uuidPath
|
||||
# we could legitimately have zeros in the new guid, so we can't check for that
|
||||
# we're just making sure that there *is* a difference
|
||||
Compare-Object -ReferenceObject $zeroGuid -DifferenceObject $newBytes | Should -Not -BeNullOrEmpty
|
||||
}
|
||||
|
||||
It "Should create a new telemetry file if the current one is smaller than 16 bytes" {
|
||||
$badBytes = [byte[]]::new(8);
|
||||
[System.IO.File]::WriteAllBytes($uuidPath, $badBytes)
|
||||
& $PWSH -NoProfile -Command "exit"
|
||||
[byte[]]$nb = Get-Content -AsByteStream -Path $uuidPath
|
||||
[System.Guid]::New($nb) | Should -BeOfType [System.Guid]
|
||||
}
|
||||
|
||||
It "Should not create a new telemetry file if the current one has a valid guid and is larger than 16 bytes" {
|
||||
$g = [Guid]::newGuid()
|
||||
$tooManyBytes = $g.ToByteArray() * 2
|
||||
[System.IO.File]::WriteAllBytes($uuidPath, $tooManyBytes)
|
||||
[byte[]]$nb = Get-Content -Path $uuidPath -AsByteStream | Select-Object -First 16
|
||||
$ng = [System.Guid]::new($nb)
|
||||
$g | Should -Be $ng
|
||||
}
|
||||
|
||||
It "Should properly set whether telemetry is sent based on when environment variable is not set" {
|
||||
$result = & $PWSH -NoProfile -Command '[Microsoft.PowerShell.Telemetry.ApplicationInsightsTelemetry]::CanSendTelemetry'
|
||||
$result | Should -Be "True"
|
||||
}
|
||||
|
||||
$telemetryIsSetData = @(
|
||||
@{ name = "set to no"; Value = "no" ; expectedValue = "True" }
|
||||
@{ name = "set to 0"; Value = "0"; expectedValue = "True" }
|
||||
@{ name = "set to false"; Value = "false"; expectedValue = "True" }
|
||||
@{ name = "set to yes"; Value = "yes"; expectedValue = "False" }
|
||||
@{ name = "set to 1"; Value = "1"; expectedValue = "False" }
|
||||
@{ name = "set to true"; Value = "true"; expectedValue = "False" }
|
||||
)
|
||||
|
||||
It "Should properly set whether telemetry is sent based on environment variable when <name>" -TestCases $telemetryIsSetData {
|
||||
param ( [string]$name, [string]$value, [string]$expectedValue )
|
||||
$env:POWERSHELL_TELEMETRY_OPTOUT = $value
|
||||
$result = & $PWSH -NoProfile -Command '[Microsoft.PowerShell.Telemetry.ApplicationInsightsTelemetry]::CanSendTelemetry'
|
||||
$result | Should -Be $expectedValue
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user