Functions/GenXdev.Helpers/UnattendedModeHelper.cs
|
// ################################################################################
// Part of PowerShell module : GenXdev.Helpers // Original cmdlet filename : UnattendedModeHelper.cs // Original author : René Vaessen / GenXdev // Version : 2.1.2025 // ################################################################################ // Copyright (c) René Vaessen / GenXdev // // 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. // See the License for the specific language governing permissions and // limitations under the License. // ################################################################################ using System.Management.Automation; namespace GenXdev.Helpers { /// <summary> /// <para type="synopsis"> /// Helper class for detecting if the current process is running in unattended/automated mode. /// </para> /// /// <para type="description"> /// This static class provides methods to determine whether the current execution environment /// is automated or interactive. It checks various indicators such as environment variables, /// console redirection, user interactivity, and PowerShell pipeline context. /// </para> /// </summary> public static class UnattendedModeHelper { /// <summary> /// <para type="synopsis"> /// Detects if the current process is running in unattended/automated mode. /// </para> /// /// <para type="description"> /// This method performs multiple checks to determine if the current execution is automated: /// - Checks for common CI/CD environment variables /// - Detects console input/output redirection /// - Verifies user interactivity /// - Analyzes console window availability /// - Examines PowerShell pipeline and invocation context (if provided) /// /// Returns true if any automation indicator is detected, false if the environment appears interactive. /// </para> /// /// <para type="description"> /// PARAMETERS /// </para> /// /// <para type="description"> /// -CallersInvocation <InvocationInfo><br/> /// Optional: The caller's InvocationInfo for pipeline and automation detection.<br/> /// - <b>Position</b>: 0<br/> /// - <b>Default</b>: null<br/> /// </para> /// /// <example> /// <para>Check if running in unattended mode</para> /// <para>This example demonstrates how to use the method to detect automation.</para> /// <code> /// bool isUnattended = UnattendedModeHelper.IsUnattendedMode(); /// </code> /// </example> /// /// <example> /// <para>Check with invocation info</para> /// <para>Pass the current invocation info for more accurate detection in PowerShell context.</para> /// <code> /// bool isUnattended = UnattendedModeHelper.IsUnattendedMode(MyInvocation); /// </code> /// </example> /// </summary> /// <param name="callersInvocation">Optional: The caller's InvocationInfo for pipeline and automation detection.</param> /// <returns>True if running in unattended/automated mode, otherwise false.</returns> public static bool IsUnattendedMode(InvocationInfo callersInvocation = null) { // Define environment variables that indicate CI/CD or automation systems string[] automationEnvVars = new[] { "JENKINS_URL", "GITHUB_ACTIONS", "TF_BUILD", "CI", "BUILD_ID", "RUNNER_OS", "SYSTEM_TEAMPROJECT", "TEAMCITY_VERSION", "TRAVIS", "APPVEYOR", "CIRCLECI", "GITLAB_CI", "AZURE_PIPELINES" }; // Check if any of these environment variables are set, indicating automation bool hasAutomationEnv = automationEnvVars.Any(envVar => !string.IsNullOrEmpty(System.Environment.GetEnvironmentVariable(envVar))); if (hasAutomationEnv) return true; // Check for console input or output redirection, common in automated scripts try { if (Console.IsInputRedirected || Console.IsOutputRedirected) return true; } catch { /* Ignore exceptions when console access fails */ } // Verify if the environment is interactive try { if (!System.Environment.UserInteractive) return true; } catch { /* Ignore exceptions in non-interactive environments */ } // Check 4: PowerShell host indicators (if available) // Not available in pure C# context without PowerShell Host, so skip // Check for absence of console window, indicating headless execution try { if (Console.WindowWidth == 0) return true; } catch { // If console access fails, assume no console window is available return true; } // Check 6: PowerShell execution parameters (not available in pure C# context) // Skipped // Analyze PowerShell pipeline and invocation context if provided if (callersInvocation != null) { int pipelineInfo = callersInvocation.PipelinePosition; int pipelineLength = callersInvocation.PipelineLength; // Determine if command is part of a pipeline bool isInPipeline = pipelineLength > 1; bool isNotPipelineEnd = pipelineInfo < pipelineLength; // Check if invoked from a script file bool isFromScript = !string.IsNullOrEmpty(callersInvocation.ScriptName); // Extract the command line for pattern analysis string commandLine = callersInvocation.Line ?? string.Empty; // Check for patterns indicating automated command execution bool isAutomatedCommand = System.Text.RegularExpressions.Regex.IsMatch(commandLine, @"^\s*(foreach|%|\||;|&)") || System.Text.RegularExpressions.Regex.IsMatch(commandLine, @"(Get-|Set-|Invoke-|Start-|Stop-).+\|") || System.Text.RegularExpressions.Regex.IsMatch(commandLine, @"\$\w+\s*\|\s*"); // Determine if this appears to be an interactive function call bool isInteractiveFunction = pipelineLength == 1 && string.IsNullOrEmpty(callersInvocation.ScriptName); // Return true if in pipeline and not an interactive function if (isInPipeline && !isInteractiveFunction) return true; // Return true if not at pipeline end and not interactive if (isNotPipelineEnd && !isInteractiveFunction) return true; // Return true if automated command pattern detected and not interactive if (isAutomatedCommand && !isInteractiveFunction) return true; } // If none of the automation indicators are present, assume interactive mode return false; } } } |