Skip to content

Client-side telemetry is opt-in by default #4396

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Aug 8, 2017
Merged
43 changes: 7 additions & 36 deletions src/Common/Commands.Common/AzureDataCmdlet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,48 +111,19 @@ protected override void SaveDataCollectionProfile()
WriteWarning(string.Format(Resources.DataCollectionSaveFileInformation, fileFullPath));
}

protected override void PromptForDataCollectionProfileIfNotExists()
protected override void SetDataCollectionProfileIfNotExists()
{
// Initialize it from the environment variable or profile file.
InitializeDataCollectionProfile();

if (!_dataCollectionProfile.EnableAzureDataCollection.HasValue && CheckIfInteractive())
if (_dataCollectionProfile.EnableAzureDataCollection.HasValue)
{
WriteWarning(Resources.DataCollectionPrompt);

const double timeToWaitInSeconds = 60;
var status = string.Format(Resources.DataCollectionConfirmTime, timeToWaitInSeconds);
ProgressRecord record = new ProgressRecord(0, Resources.DataCollectionActivity, status);

var startTime = DateTime.Now;
var endTime = DateTime.Now;
double elapsedSeconds = 0;

while (!this.Host.UI.RawUI.KeyAvailable && elapsedSeconds < timeToWaitInSeconds)
{
TestMockSupport.Delay(10 * 1000);
endTime = DateTime.Now;

elapsedSeconds = (endTime - startTime).TotalSeconds;
record.PercentComplete = ((int)elapsedSeconds * 100 / (int)timeToWaitInSeconds);
WriteProgress(record);
}

bool enabled = false;
if (this.Host.UI.RawUI.KeyAvailable)
{
KeyInfo keyInfo =
this.Host.UI.RawUI.ReadKey(ReadKeyOptions.NoEcho | ReadKeyOptions.AllowCtrlC |
ReadKeyOptions.IncludeKeyDown);
enabled = (keyInfo.Character == 'Y' || keyInfo.Character == 'y');
}

_dataCollectionProfile.EnableAzureDataCollection = enabled;
return;
}

WriteWarning(enabled ? Resources.DataCollectionConfirmYes : Resources.DataCollectionConfirmNo);
WriteWarning(Resources.RDFEDataCollectionMessage);

SaveDataCollectionProfile();
}
_dataCollectionProfile.EnableAzureDataCollection = true;
SaveDataCollectionProfile();
}

protected override void InitializeQosEvent()
Expand Down
21 changes: 15 additions & 6 deletions src/Common/Commands.Common/AzurePSCmdlet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,23 +138,32 @@ protected static void InitializeDataCollectionProfile()
if (string.Equals(value, bool.FalseString, StringComparison.OrdinalIgnoreCase))
{
// Disable data collection only if it is explicitly set to 'false'.
_dataCollectionProfile = new AzurePSDataCollectionProfile(true);
_dataCollectionProfile = new AzurePSDataCollectionProfile(false);
}
else if (string.Equals(value, bool.TrueString, StringComparison.OrdinalIgnoreCase))
{
// Enable data collection only if it is explicitly set to 'true'.
_dataCollectionProfile = new AzurePSDataCollectionProfile(false);
_dataCollectionProfile = new AzurePSDataCollectionProfile(true);
}
}

// If the environment value is null or empty, or not correctly set, try to read the setting from default file location.
if (_dataCollectionProfile == null)
{
// If it exists, remove the old AzureDataCollectionProfile.json file
string oldFileFullPath = Path.Combine(AzurePowerShell.ProfileDirectory,
AzurePSDataCollectionProfile.OldDefaultFileName);
if (AzureSession.Instance.DataStore.FileExists(oldFileFullPath))
{
AzureSession.Instance.DataStore.DeleteFile(oldFileFullPath);
}

// Try and read from the new AzurePSDataCollectionProfile.json file
string fileFullPath = Path.Combine(AzurePowerShell.ProfileDirectory,
AzurePSDataCollectionProfile.DefaultFileName);
if (File.Exists(fileFullPath))
if (AzureSession.Instance.DataStore.FileExists(fileFullPath))
{
string contents = File.ReadAllText(fileFullPath);
string contents = AzureSession.Instance.DataStore.ReadFileAsText(fileFullPath);
_dataCollectionProfile =
JsonConvert.DeserializeObject<AzurePSDataCollectionProfile>(contents);
}
Expand Down Expand Up @@ -234,7 +243,7 @@ protected bool CheckIfInteractive()
/// <summary>
/// Prompt for the current data collection profile
/// </summary>
protected abstract void PromptForDataCollectionProfileIfNotExists();
protected abstract void SetDataCollectionProfileIfNotExists();

protected virtual void LogCmdletStartInvocationInfo()
{
Expand Down Expand Up @@ -294,7 +303,7 @@ protected virtual void TearDownHttpClientPipeline()
/// </summary>
protected override void BeginProcessing()
{
PromptForDataCollectionProfileIfNotExists();
SetDataCollectionProfileIfNotExists();
InitializeQosEvent();
LogCmdletStartInvocationInfo();
SetupDebuggingTraces();
Expand Down
3 changes: 2 additions & 1 deletion src/Common/Commands.Common/AzurePSDataCollectionProfile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ namespace Microsoft.WindowsAzure.Commands.Common
public class AzurePSDataCollectionProfile
{
public const string EnvironmentVariableName = "Azure_PS_Data_Collection";
public static string DefaultFileName = "AzureDataCollectionProfile.json";
public static string OldDefaultFileName = "AzureDataCollectionProfile.json";
public static string DefaultFileName = "AzurePSDataCollectionProfile.json";

public AzurePSDataCollectionProfile()
{
Expand Down
13 changes: 13 additions & 0 deletions src/Common/Commands.Common/Extensions/CmdletExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// limitations under the License.
// ----------------------------------------------------------------------------------

using Microsoft.WindowsAzure.Commands.Common;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
Expand Down Expand Up @@ -216,6 +217,18 @@ public static void InvokeEndProcessing(this PSCmdlet cmdlt)
dynMethod.Invoke(cmdlt, null);
}

public static void EnableDataCollection(this AzurePSCmdlet cmdlt)
{
FieldInfo dynField = (typeof(AzurePSCmdlet)).GetField("_dataCollectionProfile", BindingFlags.NonPublic | BindingFlags.Static);
dynField.SetValue(cmdlt, new AzurePSDataCollectionProfile(true));
}

public static void DisableDataCollection(this AzurePSCmdlet cmdlt)
{
FieldInfo dynField = (typeof(AzurePSCmdlet)).GetField("_dataCollectionProfile", BindingFlags.NonPublic | BindingFlags.Static);
dynField.SetValue(cmdlt, new AzurePSDataCollectionProfile(false));
}

#endregion
}
}
21 changes: 21 additions & 0 deletions src/Common/Commands.Common/MetricHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.NetworkInformation;
using System.Security.Cryptography;
using System.Text;

Expand Down Expand Up @@ -52,6 +54,24 @@ public class MetricHelper
/// </summary>
private readonly object _lock = new object();

private static string _hashMacAddress = string.Empty;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not initialize to null?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted null to represent that there was no valid NIC found. I used string.Empty to represent that we haven't tried to initialize the value of _hashMacAddress yet


private static string HashMacAddress
{
get
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this an instance value instead?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved offline - let's not test the value of _hashMacAddress in the code that sets the value.

{
if (_hashMacAddress == string.Empty)
{
var macAddress = NetworkInterface.GetAllNetworkInterfaces()
.FirstOrDefault(nic => nic.OperationalStatus == OperationalStatus.Up)?
.GetPhysicalAddress()?.ToString();
_hashMacAddress = macAddress == null ? null : GenerateSha256HashString(macAddress).Replace("-", string.Empty).ToLowerInvariant();
}

return _hashMacAddress;
}
}

public MetricHelper()
{
if (TestMockSupport.RunningMocked)
Expand Down Expand Up @@ -177,6 +197,7 @@ private void PopulatePropertiesFromQos(AzurePSQoSEvent qos, IDictionary<string,
eventProperties.Add("UserId", qos.Uid);
eventProperties.Add("x-ms-client-request-id", qos.ClientRequestId);
eventProperties.Add("UserAgent", AzurePowerShell.UserAgentValue.ToString());
eventProperties.Add("HashMacAddress", HashMacAddress);
if (qos.InputFromPipeline != null)
{
eventProperties.Add("InputFromPipeline", qos.InputFromPipeline.Value.ToString());
Expand Down
43 changes: 29 additions & 14 deletions src/Common/Commands.Common/Properties/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 14 additions & 11 deletions src/Common/Commands.Common/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1468,18 +1468,13 @@ The file needs to be a PowerShell script (.ps1 or .psm1).</value>
<data name="CannotChangeBuiltinEnvironment" xml:space="preserve">
<value>Cannot change built-in environment {0}.</value>
</data>
<data name="DataCollectionPrompt" xml:space="preserve">
<value>Microsoft Azure PowerShell collects data about how users use PowerShell cmdlets and some problems they encounter. Microsoft uses this information to improve our PowerShell cmdlets. Participation is voluntary and when you choose to participate your device automatically sends information to Microsoft about how you use Azure PowerShell.

If you choose to participate, you can stop at any time by using Azure PowerShell as follows:
1. Use the Disable-AzureDataCollection cmdlet to turn the feature Off. The cmdlet can be found in the AzureRM.Profile module
To disable data collection: PS &gt; Disable-AzureDataCollection
<data name="ARMDataCollectionMessage" xml:space="preserve">
<value>Azure PowerShell collects usage data in order to improve your experience.
The data is anonymous and does not include commandline argument values.
The data is collected by Microsoft.

If you choose to not participate, you can enable at any time by using Azure PowerShell as follows:
1. Use the Enable-AzureDataCollection cmdlet to turn the feature On. The cmdlet can be found in the AzureRM.Profile module
To enable data collection: PS &gt; Enable-AzureDataCollection

Select Y to enable data collection [Y/N]:</value>
Use the Disable-AzureRmDataCollection cmdlet to turn the feature Off. The cmdlet can be found in the AzureRM.Profile module. To disable data collection: PS &gt; Disable-AzureRmDataCollection.
Use the Enable-AzureRmDataCollection cmdlet to turn the feature On. The cmdlet can be found in the AzureRM.Profile module. To enable data collection: PS &gt; Enable-AzureRmDataCollection.</value>
</data>
<data name="DataCollectionActivity" xml:space="preserve">
<value>Microsoft Azure PowerShell Data Collection Confirmation</value>
Expand Down Expand Up @@ -1586,4 +1581,12 @@ Select Y to enable data collection [Y/N]:</value>
<data name="x86InProgramFiles" xml:space="preserve">
<value>(x86)</value>
</data>
<data name="RDFEDataCollectionMessage" xml:space="preserve">
<value>Azure PowerShell collects usage data in order to improve your experience.
The data is anonymous and does not include commandline argument values.
The data is collected by Microsoft.

Use the Disable-AzureDataCollection cmdlet to turn the feature Off. The cmdlet can be found in the Azure module. To disable data collection: PS &gt; Disable-AzureDataCollection.
Use the Enable-AzureDataCollection cmdlet to turn the feature On. The cmdlet can be found in the Azure module. To enable data collection: PS &gt; Enable-AzureDataCollection.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ protected override void SaveDataCollectionProfile()
// No data collection for this commandlet
}

protected override void PromptForDataCollectionProfileIfNotExists()
protected override void SetDataCollectionProfileIfNotExists()
{
// No data collection for this commandlet
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ protected override void SaveDataCollectionProfile()
// No data collection for this commandlet
}

protected override void PromptForDataCollectionProfileIfNotExists()
protected override void SetDataCollectionProfileIfNotExists()
{
// No data collection for this commandlet
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ protected override void SaveDataCollectionProfile()
// No data collection for this commandlet
}

protected override void PromptForDataCollectionProfileIfNotExists()
protected override void SetDataCollectionProfileIfNotExists()
{
// No data collection for this commandlet
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,46 +182,19 @@ protected override void SaveDataCollectionProfile()
WriteWarning(string.Format(Resources.DataCollectionSaveFileInformation, fileFullPath));
}

protected override void PromptForDataCollectionProfileIfNotExists()
protected override void SetDataCollectionProfileIfNotExists()
{
// Initialize it from the environment variable or profile file.
InitializeDataCollectionProfile();

if (!_dataCollectionProfile.EnableAzureDataCollection.HasValue && CheckIfInteractive())
if (_dataCollectionProfile.EnableAzureDataCollection.HasValue)
{
WriteWarning(Resources.DataCollectionPrompt);

const double timeToWaitInSeconds = 60;
var status = string.Format(Resources.DataCollectionConfirmTime, timeToWaitInSeconds);
ProgressRecord record = new ProgressRecord(0, Resources.DataCollectionActivity, status);

var startTime = DateTime.Now;
var endTime = DateTime.Now;
double elapsedSeconds = 0;

while (!this.Host.UI.RawUI.KeyAvailable && elapsedSeconds < timeToWaitInSeconds)
{
Thread.Sleep(TimeSpan.FromMilliseconds(10));
endTime = DateTime.Now;

elapsedSeconds = (endTime - startTime).TotalSeconds;
record.PercentComplete = ((int)elapsedSeconds * 100 / (int)timeToWaitInSeconds);
WriteProgress(record);
}

bool enabled = false;
if (this.Host.UI.RawUI.KeyAvailable)
{
KeyInfo keyInfo = this.Host.UI.RawUI.ReadKey(ReadKeyOptions.NoEcho | ReadKeyOptions.AllowCtrlC | ReadKeyOptions.IncludeKeyDown);
enabled = (keyInfo.Character == 'Y' || keyInfo.Character == 'y');
}

_dataCollectionProfile.EnableAzureDataCollection = enabled;
return;
}

WriteWarning(enabled ? Resources.DataCollectionConfirmYes : Resources.DataCollectionConfirmNo);
WriteWarning(Resources.ARMDataCollectionMessage);

SaveDataCollectionProfile();
}
_dataCollectionProfile.EnableAzureDataCollection = true;
SaveDataCollectionProfile();
}

protected override void InitializeQosEvent()
Expand Down
Loading