From 7605317053e3cd50c1e75e87b06b35bfd99962f6 Mon Sep 17 00:00:00 2001 From: Maoliang Huang Date: Thu, 14 Jan 2021 09:15:15 -0800 Subject: [PATCH 01/12] Add prompt for usage and survey. --- .../Az.Tools.Predictor/Az.Tools.Predictor.csproj | 3 ++- .../Az.Tools.Predictor/Az.Tools.Predictor.psd1 | 2 ++ tools/Az.Tools.Predictor/Az.Tools.Predictor/ImportPrompt.ps1 | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 tools/Az.Tools.Predictor/Az.Tools.Predictor/ImportPrompt.ps1 diff --git a/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.csproj b/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.csproj index d844674d231f..016dd88379e4 100644 --- a/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.csproj +++ b/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.csproj @@ -42,7 +42,8 @@ For more information on Az Predictor, please visit the following: https://aka.ms + - + diff --git a/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.psd1 b/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.psd1 index a470dd7b64ca..eb9eef183c0e 100644 --- a/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.psd1 +++ b/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.psd1 @@ -50,6 +50,8 @@ PowerShellVersion = '7.1' NestedModules = @("Microsoft.Azure.PowerShell.Tools.AzPredictor.dll") +ScriptsToProcess = @("ImportPrompt.ps1") + # Format files (.ps1xml) to be loaded when importing this module # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. diff --git a/tools/Az.Tools.Predictor/Az.Tools.Predictor/ImportPrompt.ps1 b/tools/Az.Tools.Predictor/Az.Tools.Predictor/ImportPrompt.ps1 new file mode 100644 index 000000000000..ac412f6c0e02 --- /dev/null +++ b/tools/Az.Tools.Predictor/Az.Tools.Predictor/ImportPrompt.ps1 @@ -0,0 +1,3 @@ +Write-Host "To enable suggestions from Az predictor, run: Set-PSReadLineOption -PredictionSource HistoryAndPlugin +We are listening, please share your feedback about Az Predictor: http://aka.ms/azpredictorsurvey?iQ_CHL=intercept" + From a1616a9b9190e926916b77a63b1912d9c2f88d32 Mon Sep 17 00:00:00 2001 From: Maoliang Huang Date: Mon, 1 Feb 2021 13:58:22 -0800 Subject: [PATCH 02/12] Condition the survey and correlate survey and telemetry. - Check if the user has used the module in the past 30 days and whether the user uses at least 3 times. If so, prompt the survey. - Create a survey id based on user id and use it to associate the survey and the telemetry. - Add a field in the telemetry to check if the telemetry data is from an internal user. --- .../Az.Tools.Predictor.csproj | 4 +- .../Az.Tools.Predictor.psd1 | 4 +- .../Az.Tools.Predictor/AzContext.cs | 15 +- .../Az.Tools.Predictor/AzPredictor.cs | 8 +- .../Az.Tools.Predictor/IAzContext.cs | 5 + .../Az.Tools.Predictor/ImportPrompt.ps1 | 3 - .../Az.Tools.Predictor/InterceptSurvey.ps1 | 175 ++++++++++++++++++ .../Az.Tools.Predictor/Settings.cs | 24 ++- .../Telemetry/AzPredictorTelemetryClient.cs | 2 + 9 files changed, 230 insertions(+), 10 deletions(-) delete mode 100644 tools/Az.Tools.Predictor/Az.Tools.Predictor/ImportPrompt.ps1 create mode 100644 tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 diff --git a/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.csproj b/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.csproj index 016dd88379e4..f25a958f65ae 100644 --- a/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.csproj +++ b/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.csproj @@ -16,7 +16,7 @@ Az.Tools.Predictor - 0.1.0 + 0.1.2 Microsoft Corporation Microsoft Corporation Microsoft Corporation. All rights reserved. @@ -42,7 +42,7 @@ For more information on Az Predictor, please visit the following: https://aka.ms - + diff --git a/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.psd1 b/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.psd1 index eb9eef183c0e..4de264d016cd 100644 --- a/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.psd1 +++ b/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.psd1 @@ -11,7 +11,7 @@ RootModule = '' # Version number of this module. -ModuleVersion = '0.1.0' +ModuleVersion = '0.1.2' # Supported PSEditions CompatiblePSEditions = 'Core' @@ -50,7 +50,7 @@ PowerShellVersion = '7.1' NestedModules = @("Microsoft.Azure.PowerShell.Tools.AzPredictor.dll") -ScriptsToProcess = @("ImportPrompt.ps1") +ScriptsToProcess = @("InterceptSurvey.ps1") # Format files (.ps1xml) to be loaded when importing this module diff --git a/tools/Az.Tools.Predictor/Az.Tools.Predictor/AzContext.cs b/tools/Az.Tools.Predictor/Az.Tools.Predictor/AzContext.cs index 91e6dbc5cb87..8258dc3e9181 100644 --- a/tools/Az.Tools.Predictor/Az.Tools.Predictor/AzContext.cs +++ b/tools/Az.Tools.Predictor/Az.Tools.Predictor/AzContext.cs @@ -30,6 +30,7 @@ namespace Microsoft.Azure.PowerShell.Tools.AzPredictor /// internal sealed class AzContext : IAzContext { + private const string InternalUserSuffix = "@microsoft.com"; private static readonly Version DefaultVersion = new Version("0.0.0.0"); /// @@ -100,13 +101,25 @@ public Version ModuleVersion } } + public bool IsInternal { get; internal set; } + + internal string SurveyId { get; set; } + /// public void UpdateContext() { AzVersion = GetAzVersion(); - UserId = GenerateSha256HashString(GetUserAccountId()); + RawUserId = GetUserAccountId(); + UserId = GenerateSha256HashString(RawUserId); + + if (!IsInternal) + { + IsInternal = RawUserId.EndsWith(AzContext.InternalUserSuffix, StringComparison.OrdinalIgnoreCase); + } } + internal string RawUserId { get; set; } + /// /// Gets the user account id if the user logs in, otherwise empty string. /// diff --git a/tools/Az.Tools.Predictor/Az.Tools.Predictor/AzPredictor.cs b/tools/Az.Tools.Predictor/Az.Tools.Predictor/AzPredictor.cs index 600c388a5603..706765240a94 100644 --- a/tools/Az.Tools.Predictor/Az.Tools.Predictor/AzPredictor.cs +++ b/tools/Az.Tools.Predictor/Az.Tools.Predictor/AzPredictor.cs @@ -16,6 +16,7 @@ using Microsoft.Azure.PowerShell.Tools.AzPredictor.Utilities; using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Management.Automation; using System.Management.Automation.Language; @@ -225,7 +226,12 @@ public class PredictorInitializer : IModuleAssemblyInitializer public void OnImport() { var settings = Settings.GetSettings(); - var azContext = new AzContext(); + var azContext = new AzContext() + { + IsInternal = (settings.SetAsInternal == true) ? true : false, + SurveyId = settings.SurveyId?.ToString(CultureInfo.InvariantCulture) ?? string.Empty, + }; + azContext.UpdateContext(); var telemetryClient = new AzPredictorTelemetryClient(azContext); var azPredictorService = new AzPredictorService(settings.ServiceUri, telemetryClient, azContext); diff --git a/tools/Az.Tools.Predictor/Az.Tools.Predictor/IAzContext.cs b/tools/Az.Tools.Predictor/Az.Tools.Predictor/IAzContext.cs index 657ea8369f3c..b6e4e38de58f 100644 --- a/tools/Az.Tools.Predictor/Az.Tools.Predictor/IAzContext.cs +++ b/tools/Az.Tools.Predictor/Az.Tools.Predictor/IAzContext.cs @@ -51,6 +51,11 @@ internal interface IAzContext /// public Version AzVersion { get; } + /// + /// Gets whether the user is an internal user. + /// + public bool IsInternal { get; } + /// /// Updates the Az context. /// diff --git a/tools/Az.Tools.Predictor/Az.Tools.Predictor/ImportPrompt.ps1 b/tools/Az.Tools.Predictor/Az.Tools.Predictor/ImportPrompt.ps1 deleted file mode 100644 index ac412f6c0e02..000000000000 --- a/tools/Az.Tools.Predictor/Az.Tools.Predictor/ImportPrompt.ps1 +++ /dev/null @@ -1,3 +0,0 @@ -Write-Host "To enable suggestions from Az predictor, run: Set-PSReadLineOption -PredictionSource HistoryAndPlugin -We are listening, please share your feedback about Az Predictor: http://aka.ms/azpredictorsurvey?iQ_CHL=intercept" - diff --git a/tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 b/tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 new file mode 100644 index 000000000000..c6f824c66efe --- /dev/null +++ b/tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 @@ -0,0 +1,175 @@ +param ( + [Parameter(Mandatory)] + [string] $moduleName, + [Parameter(Mandatory)] + [int] $majorVersion +) + +if ([string]::IsNullOrWhiteSpace($moduleName)) +{ + return +} + +$mutexName = "AzModulesInterceptSurvey" +$mutexTiimeout = 1000 +$interceptDays = 30 +$interceptLoadTimes = 3 +$today = Get-Date + +function ConvertTo-String { + param ( + [Parameter(Mandatory)] + [DateTime] $date + ) + + return $date.ToString("yyyy-MM-dd") +} + +function Init-InterceptFile { + $interceptContent = @{ + "lastInterceptCheckDate"=ConvertTo-String($today); + "interceptTriggered"=$false; + "modules"=@(@{ + "name"=$moduleName; + "majorVersion"=$majorVersion; + "activeDays"=1; + "lastActiveDate"=ConvertTo-String($today); + }) + } + + ConvertTo-Json -InputObject $interceptContent | Out-File -FilePath $interceptFilePath -Encoding utf8 +} + +# Update the intercept object and return $true if we need to show the survey. +function Update-InterceptObject { + param ( + $interceptObject + ) + + $thisModule = $null + + foreach ($m in $interceptObject.modules) { + if ($m.name -eq $moduleName) { + $thisModule = $m + break + } + } + + if ($thisModule -eq $null) { + # There is no information about this module. The file could be created by another module or in some other way. + # We need to add this module to the list. + + $thisModule = @{ + "name"=$moduleName; + "majorVersion"=$majorVersion; + "activeDays"=1; + "lastActiveDate"=ConvertTo-String($today); + } + + $interceptObject.modules += $thisModule + + return $false + } + + $recordedMajorVersion = $thisModule.majorVersion + $thisModule.majorVersion = $majorVersion + + if ($recordedMajorVersion -ne $majorVersion) { + $thisModule.activeDays = 1 + $thisModule.lastActiveDate = ConvertTo-String($today) + $interceptObject.interceptTriggered = $false + + return $false + } + + $recordedLastActiveDate = Get-Date $thisModule.lastActiveDate + $recordedActiveDays = $thisModule.activeDays + + $elapsedDays = ($today - $recordedLastActiveDate).Days + + if ($elapsedDays -gt $interceptDays) { + $thisModule.activeDays = 1 + $thisModule.lastActiveDate = ConvertTo-String($today) + + return $false + } + + $newActiveDays = $recordedActiveDays + + if ($today -ne $recordedLastActiveDate) { + $newActiveDays++ + } + + if ($newActiveDays -ge $interceptLoadTimes) { + $thisModule.activeDays = 0 + $thisModule.lastActiveDate = ConvertTo-String($today) + $interceptObject.interceptTriggered = $true + return $true + } + + $thisModule.activeDays = $newActiveDays + $thisModule.lastActiveDate = ConvertTo-String($today) +} + +$mutex = New-Object System.Threading.Mutex($false, $mutexName) + +$mutex.WaitOne($mutexTimeout) +$shouldIntercept = $false + +try +{ + $interceptFilePath = Join-Path -Path (Join-Path -Path $env:USERPROFILE -ChildPath ".Azure") -ChildPath "InterceptSurvey.json" + + if (-not (Test-Path $interceptFilePath)) { + New-Item -ItemType File -Force -Path $interceptFilePath + Init-InterceptFile + } else { + $interceptObject = $null + try { + $fileContent = Get-Content $interceptFilePath | Out-String + $interceptObject = ConvertFrom-Json $fileContent + } catch { + Init-InterceptFile + } + + if (-not ($interceptObject -eq $null)) { + $shouldIntercept = Update-InterceptObject($interceptObject) + + ConvertTo-Json -InputObject $interceptObject | Out-File $interceptFilePath -Encoding utf8 + } + } +} finally +{ + $mutex.ReleaseMutex() +} + +Write-Host "To enable suggestions from Az predictor, run: Set-PSReadLineOption -PredictionSource HistoryAndPlugin" + +if ($shouldIntercept) { + $userId = (Get-AzContext).Account.Id + if ($userId -ne $null) + { + $surveyId = Get-Random -Maximum 1000000 -SetSeed $userId.GetHashCode() + Write-Host "We are listening, please share your feedback about Az Predictor: http://aka.ms/azpredictorsurvey?iQ_CHL=intercept&surveyId=$surveyId" + + try { + $azPredictorSettingFilePath = Join-Path -Path (Join-Path -Path $env:USERPROFILE -ChildPath ".Azure") -ChildPath "AzPredictorSettings.json" + $setting = @{ + "surveyId"=$surveyId; + } + + if (Test-Path $azPredictorSettingFilePath) { + try { + $setting = Get-Content $azPredictorSettingFilePath | Out-String | ConvertFrom-Json + $setting | Add-Member -NotePropertyName "surveyId" -NotePropertyValue $surveyId + } catch { + } + } + + ConvertTo-Json -InputObject $setting | Out-File -FilePath $azPredictorSettingFilePath -Encoding utf8 + } catch { + } + } else { + Write-Host "We are listening, please share your feedback about Az Predictor: http://aka.ms/azpredictorsurvey?iQ_CHL=intercept" + } +} diff --git a/tools/Az.Tools.Predictor/Az.Tools.Predictor/Settings.cs b/tools/Az.Tools.Predictor/Az.Tools.Predictor/Settings.cs index 4dfabbe10a9b..648b591284b1 100644 --- a/tools/Az.Tools.Predictor/Az.Tools.Predictor/Settings.cs +++ b/tools/Az.Tools.Predictor/Az.Tools.Predictor/Settings.cs @@ -31,16 +31,30 @@ sealed class Settings PropertyNamingPolicy = JsonNamingPolicy.CamelCase, }; + /// + /// The maximum number of suggestions that have the same command name. + /// + public int? MaxAllowedCommandDuplicate { get; set; } + /// /// The service to get the prediction results back. /// public string ServiceUri { get; set; } + /// + /// Set the user as an internal user. + /// + public bool? SetAsInternal { get; set; } + /// /// The number of suggestions to return to PSReadLine. /// public int? SuggestionCount { get; set; } - public int? MaxAllowedCommandDuplicate { get; set; } + + /// + /// The survey id. It should be internal but make it public so that we can read/write to Json. + /// + public int? SurveyId { get; set; } private static bool? _isContinueOnTimeout; /// @@ -127,6 +141,14 @@ private void OverrideSettingsFromProfile() { this.MaxAllowedCommandDuplicate = profileSettings.MaxAllowedCommandDuplicate; } + + this.SetAsInternal = profileSettings.SetAsInternal; + this.SurveyId = profileSettings.SurveyId; + + profileSettings.SurveyId = null; + + fileContent = JsonSerializer.Serialize(profileSettings, new JsonSerializerOptions(Settings._jsonSerializerOptions) { IgnoreNullValues = true }); + File.WriteAllText(profileSettingFilePath, fileContent, Encoding.UTF8); } catch { diff --git a/tools/Az.Tools.Predictor/Az.Tools.Predictor/Telemetry/AzPredictorTelemetryClient.cs b/tools/Az.Tools.Predictor/Az.Tools.Predictor/Telemetry/AzPredictorTelemetryClient.cs index cbcf3bf490b6..92b1260ec76b 100644 --- a/tools/Az.Tools.Predictor/Az.Tools.Predictor/Telemetry/AzPredictorTelemetryClient.cs +++ b/tools/Az.Tools.Predictor/Az.Tools.Predictor/Telemetry/AzPredictorTelemetryClient.cs @@ -290,6 +290,8 @@ private IDictionary CreateProperties(ITelemetryData telemetryDat { "SessionId", telemetryData.SessionId }, { "CorrelationId", telemetryData.CorrelationId }, { "UserId", _azContext.UserId }, + { "IsInternal", _azContext.IsInternal.ToString(CultureInfo.InvariantCulture) }, + { "SurveyId", (_azContext as AzContext)?.SurveyId }, { "HashMacAddress", _azContext.MacAddress }, { "PowerShellVersion", _azContext.PowerShellVersion.ToString() }, { "ModuleVersion", _azContext.ModuleVersion.ToString() }, From 7b69536e21f722488d753c9fdd911f82e28c69f4 Mon Sep 17 00:00:00 2001 From: Maoliang Huang Date: Mon, 1 Feb 2021 15:28:55 -0800 Subject: [PATCH 03/12] Wrap the intercept script in the script to process. --- .../Az.Tools.Predictor.csproj | 1 + .../Az.Tools.Predictor.psd1 | 2 +- .../Az.Tools.Predictor/InterceptSurvey.ps1 | 19 +++++++++++++------ .../Az.Tools.Predictor/PromptSurvey.ps1 | 2 ++ 4 files changed, 17 insertions(+), 7 deletions(-) create mode 100644 tools/Az.Tools.Predictor/Az.Tools.Predictor/PromptSurvey.ps1 diff --git a/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.csproj b/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.csproj index f25a958f65ae..3c9074c8d063 100644 --- a/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.csproj +++ b/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.csproj @@ -43,6 +43,7 @@ For more information on Az Predictor, please visit the following: https://aka.ms + diff --git a/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.psd1 b/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.psd1 index 4de264d016cd..e8cf2df84ce1 100644 --- a/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.psd1 +++ b/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.psd1 @@ -50,7 +50,7 @@ PowerShellVersion = '7.1' NestedModules = @("Microsoft.Azure.PowerShell.Tools.AzPredictor.dll") -ScriptsToProcess = @("InterceptSurvey.ps1") +ScriptsToProcess = @("PromptSurvey.ps1") # Format files (.ps1xml) to be loaded when importing this module diff --git a/tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 b/tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 index c6f824c66efe..0ea178a93438 100644 --- a/tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 +++ b/tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 @@ -5,8 +5,15 @@ param ( [int] $majorVersion ) -if ([string]::IsNullOrWhiteSpace($moduleName)) -{ +if ([string]::IsNullOrWhiteSpace($moduleName)) { + return +} + +if ($majorVersion -lt 0) { + return +} + +if ($env:Azure_PS_Intercept_Survey -eq "false") { return } @@ -96,7 +103,7 @@ function Update-InterceptObject { $newActiveDays = $recordedActiveDays - if ($today -ne $recordedLastActiveDate) { + if ($elapsedDays -ne 0) { $newActiveDays++ } @@ -138,11 +145,11 @@ try ConvertTo-Json -InputObject $interceptObject | Out-File $interceptFilePath -Encoding utf8 } } -} finally -{ - $mutex.ReleaseMutex() +} catch { } +$mutex.ReleaseMutex() + Write-Host "To enable suggestions from Az predictor, run: Set-PSReadLineOption -PredictionSource HistoryAndPlugin" if ($shouldIntercept) { diff --git a/tools/Az.Tools.Predictor/Az.Tools.Predictor/PromptSurvey.ps1 b/tools/Az.Tools.Predictor/Az.Tools.Predictor/PromptSurvey.ps1 new file mode 100644 index 000000000000..49a1b352446b --- /dev/null +++ b/tools/Az.Tools.Predictor/Az.Tools.Predictor/PromptSurvey.ps1 @@ -0,0 +1,2 @@ +$targetScript = (Join-Path -Path $PSScriptRoot -ChildPath "InterceptSurvey.ps1") +& $targetScript "Az.Tools.Predictor" 0 From 8233d406a84b5c60c4ebcf541c142f404dc143ad Mon Sep 17 00:00:00 2001 From: Maoliang Huang Date: Mon, 8 Feb 2021 11:54:33 -0800 Subject: [PATCH 04/12] Update the link and message color. --- .../Az.Tools.Predictor/InterceptSurvey.ps1 | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 b/tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 index 0ea178a93438..82a7ca9a6975 100644 --- a/tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 +++ b/tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 @@ -22,6 +22,7 @@ $mutexTiimeout = 1000 $interceptDays = 30 $interceptLoadTimes = 3 $today = Get-Date +$mutexTimeout = 500 function ConvertTo-String { param ( @@ -120,7 +121,12 @@ function Update-InterceptObject { $mutex = New-Object System.Threading.Mutex($false, $mutexName) -$mutex.WaitOne($mutexTimeout) +$hasMutex = $mutex.WaitOne($mutexTimeout) + +if (-not $hasMutex) { + return +} + $shouldIntercept = $false try @@ -154,11 +160,11 @@ Write-Host "To enable suggestions from Az predictor, run: Set-PSReadLineOption - if ($shouldIntercept) { $userId = (Get-AzContext).Account.Id + $surveyId = "000000" + if ($userId -ne $null) { $surveyId = Get-Random -Maximum 1000000 -SetSeed $userId.GetHashCode() - Write-Host "We are listening, please share your feedback about Az Predictor: http://aka.ms/azpredictorsurvey?iQ_CHL=intercept&surveyId=$surveyId" - try { $azPredictorSettingFilePath = Join-Path -Path (Join-Path -Path $env:USERPROFILE -ChildPath ".Azure") -ChildPath "AzPredictorSettings.json" $setting = @{ @@ -168,7 +174,7 @@ if ($shouldIntercept) { if (Test-Path $azPredictorSettingFilePath) { try { $setting = Get-Content $azPredictorSettingFilePath | Out-String | ConvertFrom-Json - $setting | Add-Member -NotePropertyName "surveyId" -NotePropertyValue $surveyId + $setting | Add-Member -NotePropertyName "surveyId" -NotePropertyValue $surveyId -Force } catch { } } @@ -176,7 +182,7 @@ if ($shouldIntercept) { ConvertTo-Json -InputObject $setting | Out-File -FilePath $azPredictorSettingFilePath -Encoding utf8 } catch { } - } else { - Write-Host "We are listening, please share your feedback about Az Predictor: http://aka.ms/azpredictorsurvey?iQ_CHL=intercept" } + + Write-Host "How was your experience using Az predictor? " -NoNewLine -ForegroundColor $host.privatedata.WarningForegroundColor -BackgroundColor $host.privatedata.WarningBackgroundColor ; Write-Host "http://aka.ms/azpredictorisurvey?SessionId=$surveyId" -ForegroundColor Cyan -BackgroundColor $host.privatedata.WarningBackgroundColor } From 031c9ea9edd956e71bce31366af92e92a8f1876d Mon Sep 17 00:00:00 2001 From: Maoliang Huang Date: Tue, 9 Feb 2021 10:19:35 -0800 Subject: [PATCH 05/12] Update the survey prompt UI. --- .../Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 b/tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 index 82a7ca9a6975..136e1a86ba3b 100644 --- a/tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 +++ b/tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 @@ -184,5 +184,7 @@ if ($shouldIntercept) { } } - Write-Host "How was your experience using Az predictor? " -NoNewLine -ForegroundColor $host.privatedata.WarningForegroundColor -BackgroundColor $host.privatedata.WarningBackgroundColor ; Write-Host "http://aka.ms/azpredictorisurvey?SessionId=$surveyId" -ForegroundColor Cyan -BackgroundColor $host.privatedata.WarningBackgroundColor + $escape = $([char]27) + Write-Host "`n$escape[7mHow was your experience using Az predictor? $escape[27m`n" -NoNewline; Write-Host "$escape[7mhttp://aka.ms/azpredictorisurvey?SessionId=$surveyId$escape[27m" -NoNewline + Write-Host "`n" } From 3909954bbba415fe214f0529484b89018ee004a3 Mon Sep 17 00:00:00 2001 From: Damien Caro Date: Tue, 9 Feb 2021 12:03:13 -0800 Subject: [PATCH 06/12] Updated module description --- .../Az.Tools.Predictor/Az.Tools.Predictor.psd1 | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.psd1 b/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.psd1 index e8cf2df84ce1..a68d4200e4e4 100644 --- a/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.psd1 +++ b/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.psd1 @@ -29,16 +29,11 @@ CompanyName = 'Microsoft Corporation' Copyright = 'Microsoft Corporation. All rights reserved.' # Description of the functionality provided by this module -Description = 'Microsoft Azure PowerShell - Module providing recommendations to PSReadLine v2.2.0 or above for cmdlets comprised in the Az module - This module is compatible with PowerShell 7.1 or above. +Description = 'Microsoft Azure PowerShell - Module providing recommendations for cmdlets comprised in the Az module - This module is compatible with PowerShell 7.1 or above. -The module needs to be imported manually via -Import-Module Az.Tools.Predictor - -Enable plugins via -Set-PSReadLineOption -PredictionSource HistoryAndPlugin - -Switch the output format of suggestions to list view via -Set-PSReadLineOption -PredictionViewStyle ListView +The suggestions must be activated: +- Enable-AzPredictor: Activate the suggestions +- Disable-AzPredictor: Disable the suggestions For more information on Az Predictor, please visit the following: https://aka.ms/azpredictordocs' @@ -60,7 +55,7 @@ PrivateData = @{ PSData = @{ # Tags applied to this module. These help with module discovery in online galleries. - Tags = 'Azure','PowerShell','Prediction' + Tags = 'Azure','PowerShell','Prediction', 'Recommendation', 'Az Predictor' # A URL to the license for this module. LicenseUri = 'https://aka.ms/azps-license' From 9b25a8b74014594f6e31cf8d39f2389d1f684ce4 Mon Sep 17 00:00:00 2001 From: Maoliang Huang Date: Thu, 11 Feb 2021 15:44:26 -0800 Subject: [PATCH 07/12] Revert the version and add copyright. --- .../Az.Tools.Predictor/Az.Tools.Predictor.csproj | 2 +- .../Az.Tools.Predictor/Az.Tools.Predictor.psd1 | 6 +++--- .../Az.Tools.Predictor/InterceptSurvey.ps1 | 14 ++++++++++++++ .../Az.Tools.Predictor/PromptSurvey.ps1 | 14 ++++++++++++++ 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.csproj b/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.csproj index 3c9074c8d063..6fdf7d4a30cf 100644 --- a/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.csproj +++ b/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.csproj @@ -16,7 +16,7 @@ Az.Tools.Predictor - 0.1.2 + 0.1.0 Microsoft Corporation Microsoft Corporation Microsoft Corporation. All rights reserved. diff --git a/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.psd1 b/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.psd1 index a68d4200e4e4..e824e8e9ceb4 100644 --- a/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.psd1 +++ b/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.psd1 @@ -11,7 +11,7 @@ RootModule = '' # Version number of this module. -ModuleVersion = '0.1.2' +ModuleVersion = '0.1.0' # Supported PSEditions CompatiblePSEditions = 'Core' @@ -29,11 +29,11 @@ CompanyName = 'Microsoft Corporation' Copyright = 'Microsoft Corporation. All rights reserved.' # Description of the functionality provided by this module -Description = 'Microsoft Azure PowerShell - Module providing recommendations for cmdlets comprised in the Az module - This module is compatible with PowerShell 7.1 or above. +Description = 'Microsoft Azure PowerShell - Module providing recommendations for cmdlets comprised in the Az module - This module is compatible with PowerShell 7.2 or above. The suggestions must be activated: - Enable-AzPredictor: Activate the suggestions -- Disable-AzPredictor: Disable the suggestions +- Disable-AzPredictor: Disable the suggestions For more information on Az Predictor, please visit the following: https://aka.ms/azpredictordocs' diff --git a/tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 b/tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 index 136e1a86ba3b..06934eb77b04 100644 --- a/tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 +++ b/tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 @@ -1,3 +1,17 @@ +# ---------------------------------------------------------------------------------- +# +# Copyright Microsoft Corporation +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.internal +# See the License for the specific language governing permissions and +# limitations under the License. +# ---------------------------------------------------------------------------------- + param ( [Parameter(Mandatory)] [string] $moduleName, diff --git a/tools/Az.Tools.Predictor/Az.Tools.Predictor/PromptSurvey.ps1 b/tools/Az.Tools.Predictor/Az.Tools.Predictor/PromptSurvey.ps1 index 49a1b352446b..05f6f1ce9245 100644 --- a/tools/Az.Tools.Predictor/Az.Tools.Predictor/PromptSurvey.ps1 +++ b/tools/Az.Tools.Predictor/Az.Tools.Predictor/PromptSurvey.ps1 @@ -1,2 +1,16 @@ +# ---------------------------------------------------------------------------------- +# +# Copyright Microsoft Corporation +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.internal +# See the License for the specific language governing permissions and +# limitations under the License. +# ---------------------------------------------------------------------------------- + $targetScript = (Join-Path -Path $PSScriptRoot -ChildPath "InterceptSurvey.ps1") & $targetScript "Az.Tools.Predictor" 0 From ee4565a533c1d72ef6ce10636901c26a7e3fa5ab Mon Sep 17 00:00:00 2001 From: Maoliang Huang Date: Tue, 16 Feb 2021 14:56:31 -0800 Subject: [PATCH 08/12] Incorporate feedback. --- tools/Az.Tools.Predictor/Az.Tools.Predictor/AzContext.cs | 7 +++++++ .../Az.Tools.Predictor/InterceptSurvey.ps1 | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/tools/Az.Tools.Predictor/Az.Tools.Predictor/AzContext.cs b/tools/Az.Tools.Predictor/Az.Tools.Predictor/AzContext.cs index 8258dc3e9181..bcb6a15a8802 100644 --- a/tools/Az.Tools.Predictor/Az.Tools.Predictor/AzContext.cs +++ b/tools/Az.Tools.Predictor/Az.Tools.Predictor/AzContext.cs @@ -101,8 +101,15 @@ public Version ModuleVersion } } + /// public bool IsInternal { get; internal set; } + /// + /// The survey session id appended to the survey. + /// + /// + /// We only collect this information in the preview. So make it internal and not to make it to the interface. + /// internal string SurveyId { get; set; } /// diff --git a/tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 b/tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 index 06934eb77b04..543fde238617 100644 --- a/tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 +++ b/tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 @@ -12,6 +12,13 @@ # limitations under the License. # ---------------------------------------------------------------------------------- +# This file is a temporary approach to prompt the user for a survey. +# It doesn't cover every case well or not tested well: +# 1. Allow two or more modules to show the survey link. +# 2. When the major version is changed. +# 3. Not sure about the way to handle survey id or if it's needed in future. +# 4. The file format is also subject to change in future. + param ( [Parameter(Mandatory)] [string] $moduleName, From b1c8583ae879a521b2dffd1fc8efaad9cf7d9200 Mon Sep 17 00:00:00 2001 From: Maoliang Huang Date: Tue, 16 Feb 2021 15:30:40 -0800 Subject: [PATCH 09/12] Incorporate feedback. --- .../Az.Tools.Predictor/Az.Tools.Predictor.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.psd1 b/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.psd1 index 88e7e42fcfdd..5528043a075f 100644 --- a/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.psd1 +++ b/tools/Az.Tools.Predictor/Az.Tools.Predictor/Az.Tools.Predictor.psd1 @@ -57,7 +57,7 @@ PrivateData = @{ PSData = @{ # Tags applied to this module. These help with module discovery in online galleries. - Tags = 'Azure','PowerShell','Prediction', 'Recommendation', 'Az Predictor' + Tags = 'Azure', 'PowerShell', 'Prediction', 'Recommendation', 'Az Predictor' # A URL to the license for this module. LicenseUri = 'https://aka.ms/azps-license' From 99d34cdee9f0ac5878bf255800fd08c1b3377da2 Mon Sep 17 00:00:00 2001 From: Maoliang Huang Date: Thu, 18 Feb 2021 12:30:46 -0800 Subject: [PATCH 10/12] Improve the comment. --- tools/Az.Tools.Predictor/Az.Tools.Predictor/AzContext.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/Az.Tools.Predictor/Az.Tools.Predictor/AzContext.cs b/tools/Az.Tools.Predictor/Az.Tools.Predictor/AzContext.cs index bcb6a15a8802..72f70a6d85c1 100644 --- a/tools/Az.Tools.Predictor/Az.Tools.Predictor/AzContext.cs +++ b/tools/Az.Tools.Predictor/Az.Tools.Predictor/AzContext.cs @@ -108,7 +108,8 @@ public Version ModuleVersion /// The survey session id appended to the survey. /// /// - /// We only collect this information in the preview. So make it internal and not to make it to the interface. + /// We only collect this information in the preview and it'll be removed in GA. That's why it's not defined in the + /// interface IAzContext and it's internal. /// internal string SurveyId { get; set; } From d563985c225d78b627ddf988b0e02e483d530da8 Mon Sep 17 00:00:00 2001 From: Maoliang Huang Date: Thu, 18 Feb 2021 14:36:58 -0800 Subject: [PATCH 11/12] Use a new flag to ignore null value in json serialization. --- tools/Az.Tools.Predictor/Az.Tools.Predictor/Settings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/Az.Tools.Predictor/Az.Tools.Predictor/Settings.cs b/tools/Az.Tools.Predictor/Az.Tools.Predictor/Settings.cs index 648b591284b1..2c3f9e8a8ead 100644 --- a/tools/Az.Tools.Predictor/Az.Tools.Predictor/Settings.cs +++ b/tools/Az.Tools.Predictor/Az.Tools.Predictor/Settings.cs @@ -147,7 +147,7 @@ private void OverrideSettingsFromProfile() profileSettings.SurveyId = null; - fileContent = JsonSerializer.Serialize(profileSettings, new JsonSerializerOptions(Settings._jsonSerializerOptions) { IgnoreNullValues = true }); + fileContent = JsonSerializer.Serialize(profileSettings, new JsonSerializerOptions(Settings._jsonSerializerOptions) { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }); File.WriteAllText(profileSettingFilePath, fileContent, Encoding.UTF8); } catch From 54796a7597eb28de679fbfa62992ac7409d951de Mon Sep 17 00:00:00 2001 From: Maoliang Huang Date: Thu, 18 Feb 2021 14:42:03 -0800 Subject: [PATCH 12/12] Remove the prompt about psreadlineoption. - It's replaced with cmdlets. --- tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 | 2 -- 1 file changed, 2 deletions(-) diff --git a/tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 b/tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 index 543fde238617..79b06848a9af 100644 --- a/tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 +++ b/tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1 @@ -177,8 +177,6 @@ try $mutex.ReleaseMutex() -Write-Host "To enable suggestions from Az predictor, run: Set-PSReadLineOption -PredictionSource HistoryAndPlugin" - if ($shouldIntercept) { $userId = (Get-AzContext).Account.Id $surveyId = "000000"