private PowerShellResult InvokePowerShell(PSCommand command) { using (var powerShell = System.Management.Automation.PowerShell.Create()) { powerShell.RunspacePool = _analysisRunspacePool; powerShell.Commands = command; PowerShellResult result = null; try { Collection <PSObject> output = InvokePowerShellWithModulePathPreservation(powerShell); PSDataCollection <ErrorRecord> errors = powerShell.Streams.Error; result = new PowerShellResult(output, errors, powerShell.HadErrors); } catch (CommandNotFoundException ex) { // This exception is possible if the module path loaded // is wrong even though PSScriptAnalyzer is available as a module _logger.LogError(ex.Message); } catch (CmdletInvocationException ex) { // We do not want to crash EditorServices for exceptions caused by cmdlet invocation. // Two main reasons that cause the exception are: // * PSCmdlet.WriteOutput being called from another thread than Begin/Process // * CompositionContainer.ComposeParts complaining that "...Only one batch can be composed at a time" _logger.LogError(ex.Message); } return(result); } }
private async Task <ScriptFileMarker[]> GetSemanticMarkersFromCommandAsync(PSCommand command, Runspace runspace) { PowerShellResult result = await InvokePowerShellAsync(command, runspace).ConfigureAwait(false); IReadOnlyCollection <PSObject> diagnosticResults = result?.Output ?? s_emptyDiagnosticResult; _logger.LogDebug(String.Format("Found {0} violations", diagnosticResults.Count)); var scriptMarkers = new ScriptFileMarker[diagnosticResults.Count]; int i = 0; foreach (PSObject diagnostic in diagnosticResults) { // scriptMarkers[i] = ScriptFileMarker.FromDiagnosticRecord(diagnostic); scriptMarkers[i] = ScriptFileMarker.FromUpgradePlan(diagnostic); if (scriptMarkers[i].RuleName != "ReadyToUpgradeCmdletParameter") { i++; } } Array.Resize(ref scriptMarkers, i); return(scriptMarkers); }
public async Task When_installing() { var mockApp = GetMock <IApp>(); mockApp.SetupGet(x => x.AppId).Returns(RandomString()); mockApp.SetupGet(x => x.InstallScript).Returns(RandomString()); mockApp.SetupGet(x => x.VerificationScript).Returns(RandomString()); var app = mockApp.Object; var verificationResultPreInstall = new PowerShellResult { AsString = "False" }; var desktopSystemEntriesPreInstall = new List <string> { RandomString(), }; var desktopSystemEntriesAddedDuringInstall = new List <string> { RandomString(), RandomString(), }; var desktopSystemEntriesPostInstall = desktopSystemEntriesPreInstall.Union(desktopSystemEntriesAddedDuringInstall).ToList(); bool isPreInstall = true; GetMock <IDesktopRepository>().Setup(x => x.LoadSystemEntries()).Returns(() => { if (isPreInstall) { isPreInstall = false; return(desktopSystemEntriesPreInstall); } return(desktopSystemEntriesPostInstall); }); GetMock <IPowerShell>().Setup(x => x.ExecuteAsync(app.VerificationScript !)) .ReturnsAsync(verificationResultPreInstall); await BecauseAsync(() => ClassUnderTest.InstallOrUpgradeAsync(app)); It("logs", () => { GetMock <IConsoleLogger>().Verify(x => x.Info($"Installing '{app.AppId}'")); GetMock <IConsoleLogger>().Verify(x => x.Result($"Installed '{app.AppId}'")); }); It("runs the install script", () => { GetMock <IPowerShell>().Verify(x => x.ExecuteAsync(app.VerificationScript !), Times.Exactly(2)); GetMock <IPowerShell>().Verify(x => x.ExecuteAsync(app.InstallScript)); });
/// <summary> /// Format a given script text with default codeformatting settings. /// </summary> /// <param name="scriptDefinition">Script text to be formatted</param> /// <param name="settings">ScriptAnalyzer settings</param> /// <param name="rangeList">The range within which formatting should be applied.</param> /// <returns>The formatted script text.</returns> public async Task <string> FormatAsync( string scriptDefinition, Hashtable settings, int[] rangeList) { // We cannot use Range type therefore this workaround of using -1 default value. // Invoke-Formatter throws a ParameterBinderValidationException if the ScriptDefinition is an empty string. if (string.IsNullOrEmpty(scriptDefinition)) { return(null); } var argsDict = new Dictionary <string, object> { { "ScriptDefinition", scriptDefinition }, { "Settings", settings } }; if (rangeList != null) { argsDict.Add("Range", rangeList); } PowerShellResult result = await InvokePowerShellAsync("Invoke-Formatter", argsDict); if (result == null) { _logger.Write(LogLevel.Error, "Formatter returned null result"); return(null); } if (result.HasErrors) { var errorBuilder = new StringBuilder().Append(s_indentJoin); foreach (ErrorRecord err in result.Errors) { errorBuilder.Append(err).Append(s_indentJoin); } _logger.Write(LogLevel.Warning, $"Errors found while formatting file: {errorBuilder}"); return(null); } foreach (PSObject resultObj in result.Output) { string formatResult = resultObj?.BaseObject as string; if (formatResult != null) { return(formatResult); } } return(null); }
private async Task <PSObject[]> GetDiagnosticRecordsAsync <TSettings>( string scriptContent, string[] rules, TSettings settings) where TSettings : class { var diagnosticRecords = s_emptyDiagnosticResult; // When a new, empty file is created there are by definition no issues. // Furthermore, if you call Invoke-ScriptAnalyzer with an empty ScriptDefinition // it will generate a ParameterBindingValidationException. if (string.IsNullOrEmpty(scriptContent)) { return(diagnosticRecords); } if (typeof(TSettings) == typeof(string) || typeof(TSettings) == typeof(Hashtable)) { //Use a settings file if one is provided, otherwise use the default rule list. string settingParameter; object settingArgument; if (settings != null) { settingParameter = "Settings"; settingArgument = settings; } else { settingParameter = "IncludeRule"; settingArgument = rules; } PowerShellResult result = await InvokePowerShellAsync( "Invoke-ScriptAnalyzer", new Dictionary <string, object> { { "ScriptDefinition", scriptContent }, { settingParameter, settingArgument }, // We ignore ParseErrors from PSSA because we already send them when we parse the file. { "Severity", new [] { ScriptFileMarkerLevel.Error, ScriptFileMarkerLevel.Information, ScriptFileMarkerLevel.Warning } } }); diagnosticRecords = result?.Output; } _logger.Write( LogLevel.Verbose, String.Format("Found {0} violations", diagnosticRecords.Count())); return(diagnosticRecords); }
/// <summary> /// Format a script given its contents. /// </summary> /// <param name="scriptDefinition">The full text of a script.</param> /// <param name="formatSettings">The formatter settings to use.</param> /// <param name="rangeList">A possible range over which to run the formatter.</param> /// <returns></returns> public async Task <string> FormatAsync(string scriptDefinition, Hashtable formatSettings, int[] rangeList) { // We cannot use Range type therefore this workaround of using -1 default value. // Invoke-Formatter throws a ParameterBinderValidationException if the ScriptDefinition is an empty string. if (string.IsNullOrEmpty(scriptDefinition)) { _logger.LogDebug("Script Definition was: " + scriptDefinition == null ? "null" : "empty string"); return(scriptDefinition); } var psCommand = new PSCommand() .AddCommand("Invoke-Formatter") .AddParameter("ScriptDefinition", scriptDefinition) .AddParameter("Settings", formatSettings); if (rangeList != null) { psCommand.AddParameter("Range", rangeList); } PowerShellResult result = await InvokePowerShellAsync(psCommand).ConfigureAwait(false); if (result == null) { _logger.LogError("Formatter returned null result"); return(scriptDefinition); } if (result.HasErrors) { var errorBuilder = new StringBuilder().Append(s_indentJoin); foreach (ErrorRecord err in result.Errors) { errorBuilder.Append(err).Append(s_indentJoin); } _logger.LogWarning($"Errors found while formatting file: {errorBuilder}"); return(scriptDefinition); } foreach (PSObject resultObj in result.Output) { if (resultObj?.BaseObject is string formatResult) { return(formatResult); } } _logger.LogError("Couldn't get result from output. Returning original script."); return(scriptDefinition); }
/// <summary> /// Returns a list of builtin-in PSScriptAnalyzer rules /// </summary> public IEnumerable <string> GetPSScriptAnalyzerRules() { PowerShellResult getRuleResult = InvokePowerShell("Get-ScriptAnalyzerRule"); if (getRuleResult == null) { _logger.Write(LogLevel.Warning, "Get-ScriptAnalyzerRule returned null result"); return(s_emptyGetRuleResult); } var ruleNames = new List <string>(); foreach (var rule in getRuleResult.Output) { ruleNames.Add((string)rule.Members["RuleName"].Value); } return(ruleNames); }
/// <summary> /// Returns a list of builtin-in PSScriptAnalyzer rules /// </summary> private IEnumerable <string> GetPSScriptAnalyzerRules() { PowerShellResult getRuleResult = InvokePowerShell(new PSCommand().AddCommand("Get-ScriptAnalyzerRule")); if (getRuleResult == null) { _logger.LogWarning("Get-ScriptAnalyzerRule returned null result"); return(Enumerable.Empty <string>()); } var ruleNames = new List <string>(getRuleResult.Output.Count); foreach (var rule in getRuleResult.Output) { ruleNames.Add((string)rule.Members["RuleName"].Value); } return(ruleNames); }
private async Task <ScriptFileMarker[]> GetSemanticMarkersFromCommandAsync(PSCommand command) { PowerShellResult result = await InvokePowerShellAsync(command).ConfigureAwait(false); IReadOnlyCollection <PSObject> diagnosticResults = result?.Output ?? s_emptyDiagnosticResult; _logger.LogDebug(String.Format("Found {0} violations", diagnosticResults.Count)); var scriptMarkers = new ScriptFileMarker[diagnosticResults.Count]; int i = 0; foreach (PSObject diagnostic in diagnosticResults) { scriptMarkers[i] = ScriptFileMarker.FromDiagnosticRecord(diagnostic); i++; } return(scriptMarkers); }
private PowerShellResult InvokePowerShell(string command, IDictionary <string, object> paramArgMap = null) { using (var powerShell = System.Management.Automation.PowerShell.Create()) { powerShell.RunspacePool = _analysisRunspacePool; powerShell.AddCommand(command); if (paramArgMap != null) { foreach (KeyValuePair <string, object> kvp in paramArgMap) { powerShell.AddParameter(kvp.Key, kvp.Value); } } PowerShellResult result = null; try { PSObject[] output = powerShell.Invoke().ToArray(); ErrorRecord[] errors = powerShell.Streams.Error.ToArray(); result = new PowerShellResult(output, errors, powerShell.HadErrors); } catch (CommandNotFoundException ex) { // This exception is possible if the module path loaded // is wrong even though PSScriptAnalyzer is available as a module _logger.Write(LogLevel.Error, ex.Message); } catch (CmdletInvocationException ex) { // We do not want to crash EditorServices for exceptions caused by cmdlet invocation. // Two main reasons that cause the exception are: // * PSCmdlet.WriteOutput being called from another thread than Begin/Process // * CompositionContainer.ComposeParts complaining that "...Only one batch can be composed at a time" _logger.Write(LogLevel.Error, ex.Message); } return(result); } }
public async Task When_setting_execution_policy() { var executionPolicy = "RemoteSigned"; var powerShellResult = new PowerShellResult { AsString = RandomString() }; GetMock <IPowerShell>().Setup(x => x.ExecuteAsync(Is <string>(y => y.Contains(executionPolicy)))).ReturnsAsync(powerShellResult); await BecauseAsync(() => ClassUnderTest.SetExecutionPolicyAsync()); It("reports the set policy for PowerShell", () => { GetMock <IConsoleLogger>().Verify(x => x.Result($"PowerShell - Execution Policy: {powerShellResult.AsString}")); }); It("sets and reports the set policy for Windows PowerShell", () => { GetMock <IWindowsPowerShell>().Verify(x => x.Execute(Is <string>(y => y.Contains("RemoteSigned")))); GetMock <IConsoleLogger>().Verify(x => x.Result($"Windows PowerShell - Execution Policy: {executionPolicy}")); }); }
public async Task When_downloading() { var argsJson = RandomString(); var args = new GitHubAssetDownloaderArgs { User = RandomString(), Repo = RandomString(), Extension = RandomString() }; var powerShellResult = new PowerShellResult { AsString = RandomString() }; var assetInfo = new GitHubAssetInfo { Filename = RandomString(), Url = RandomString() }; var expectedDownloadedFilePath = RandomString(); GetMock <IJsonSerializer>().Setup(x => x.Deserialize <GitHubAssetDownloaderArgs>(argsJson)).Returns(args); GetMock <IPowerShell>().Setup(x => x.ExecuteAsync(Moq.It.Is <string>(y => y.Contains(args.User) && y.Contains(args.Repo) && y.Contains(args.Extension)))) .ReturnsAsync(powerShellResult); GetMock <IJsonSerializer>().Setup(x => x.Deserialize <GitHubAssetInfo>(powerShellResult.AsString)) .Returns(assetInfo); GetMock <IResourceDownloader>().Setup(x => x.DownloadAsync(assetInfo.Url, assetInfo.Filename)) .ReturnsAsync(expectedDownloadedFilePath); var downloadedFilePath = await BecauseAsync(() => ClassUnderTest.DownloadAsync(argsJson)); It("returns the downloaded file's path", () => { downloadedFilePath.ShouldBe(expectedDownloadedFilePath); }); }
/// <summary> /// Log the features available from the PSScriptAnalyzer module that has been imported /// for use with the AnalysisService. /// </summary> private void LogAvailablePssaFeatures() { // Save ourselves some work here var featureLogLevel = LogLevel.Verbose; if (_logger.MinimumConfiguredLogLevel > featureLogLevel) { return; } // If we already know the module that was imported, save some work if (_pssaModuleInfo == null) { PowerShellResult getModuleResult = InvokePowerShell( "Get-Module", new Dictionary <string, object> { { "Name", PSSA_MODULE_NAME } }); if (getModuleResult == null) { throw new AnalysisServiceLoadException("Get-Module call to find PSScriptAnalyzer module failed"); } _pssaModuleInfo = getModuleResult.Output .Select(m => m.BaseObject) .OfType <PSModuleInfo>() .FirstOrDefault(); } if (_pssaModuleInfo == null) { throw new AnalysisServiceLoadException("Unable to find loaded PSScriptAnalyzer module for logging"); } var sb = new StringBuilder(); sb.AppendLine("PSScriptAnalyzer successfully imported:"); // Log version sb.Append(" Version: "); sb.AppendLine(_pssaModuleInfo.Version.ToString()); // Log exported cmdlets sb.AppendLine(" Exported Cmdlets:"); foreach (string cmdletName in _pssaModuleInfo.ExportedCmdlets.Keys.OrderBy(name => name)) { sb.Append(" "); sb.AppendLine(cmdletName); } // Log available rules sb.AppendLine(" Available Rules:"); foreach (string ruleName in GetPSScriptAnalyzerRules()) { sb.Append(" "); sb.AppendLine(ruleName); } _logger.Write(featureLogLevel, sb.ToString()); }