diff --git a/Setup-Profile.ps1 b/Setup-Profile.ps1 index 56477d0..fc5e846 100644 --- a/Setup-Profile.ps1 +++ b/Setup-Profile.ps1 @@ -36,148 +36,10 @@ function ProfileCode_post_common { # This loads the personal profile section from the $MyPSScriptRoot/profile.d directory Get-Command Reload-MyScripts -ErrorAction SilentlyContinue | ForEach-Object { . $_.Name } } - -function ProfileCode_common { -function Get-PowerShellPath { - Get-Process -PID $PID | ForEach-Object { $_.Path,$_.Parent.Path } | Where-Object { $_ -match 'powershell|pwsh' } | Select-Object -First 1 -} - -function ConvertTo-Base64 { -param([string]$String) - return [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($String)) -} - -function Invoke-ExpressionEx() { -[CmdletBinding(SupportsShouldProcess)]param( - [switch]$sudo, - [switch]$Force, - [string]$Function, - [Parameter(Position = 1, ValueFromRemainingArguments = $true)] - [string[]]$expr -) - - if( $sudo -and -not $(Test-IsAdmin) ) { - $local:tmpFile = $null - if( $Function ) { - $tmpFile = New-TemporaryFile -WhatIf:$false - $tmpFile = Move-Item $tmpFile.FullName "$($tmpFile.FullName).ps1" -PassThru -WhatIf:$false - Export-FunctionSource $Function | Out-File $tmpFile.FullName -WhatIf:$false - $expr = @( ".", $tmpFile.FullName, ";", $Function ) + $expr - } - - $expr = @( - '$WhatIfPreference', '=', "`$$WhatIfPreference", ';', - '$VerbosePreference', '=', "'$VerbosePreference'", ";", - '$ErrorActionPreference', '=', "'$ErrorActionPreference'", ";", - '$ConfirmPreference', '=', "'$ConfirmPreference'", ";" - ) + $expr - - $local:sudo_params = $expr -join ' ' - Write-Verbose "Perofrming the following command line via SUDO:`n{$sudo_params}" - $local:base64command = ConvertTo-Base64 $sudo_params - /usr/bin/env sudo $(Get-PowerShellPath) -EncodedCommand $base64command - if( $tmpFile ) { Remove-Item $tmpFile } - return - } - - Write-Verbose "Perofrming the following expression in-line:`n{$($expr -join ' ')}" - Invoke-Expression "$expr" -} - -function Test-IsAdmin{ -[CmdletBinding(ConfirmImpact = 'None')] -[OutputType([bool])] -param () - - switch($true){ - $( ($PSVersionTable.PSEdition -eq 'Desktop') -or - ($PSVersionTable.Platform -eq 'Win32NT') - ) { - # Fastest way on Windows - ([Security.Principal.WindowsPrincipal]( - [Security.Principal.WindowsIdentity]::GetCurrent() - )).IsInRole( - [Security.Principal.WindowsBuiltInRole]'Administrator' - ) - } - $( ($PSVersionTable.PSEdition -eq 'Core') -and - ($PSVersionTable.Platform -eq 'Unix') - ) { - # On macOS and Linux we use ID to figure out if we run elevated (0 means superuser rights) - return $((id -u) -eq 0) - } - default { - # Unable to figure it out! - Write-Warning -Message 'Unknown' - - return - } - } - -} - -function Export-FunctionSource { -param( - [switch]$NoHeader, - [Parameter(Position = 0, ValueFromRemainingArguments = $true)] - #[ValidateSet([Functions])] - [string[]]$FunctionName - -) - - $local:src = "" - foreach( $local:func in $FunctionName ) { - if( -not $NoHeader ) { $src += "`nfunction $func {" } - $src += "`n" - $src += $((Get-Command -Type Function $func).Definition) ### .Replace(('$'+'_'),('$$'+'_')) - if( -not $NoHeader ) { $src += "`n}" } - $src += "`n" - } - return $src.Trim() -} - -# Are we running in Unix or Windows? -$global:PathEnvDelimiter = $(if( $PSVersionTable.Platform -match 'unix' ) {':'} else {';'}) -function Split-PathEnv { -param([string]$EnvPath) - $EnvPath -split $PathEnvDelimiter -} - -# Establish Module Path -$global:MyPSModulePath = Split-PathEnv $env:PSModulePath | Where-Object { $_ -match "^$($(Resolve-Path ~) -replace '\\',"\\")" } - -if( -not $MyPSModulePath ) { - $MyPSModulePath = $(Join-Path $(Join-Path $(Resolve-Path ~) 'powershell') 'Modules') - $env:PSModulePath = "$MyPSModulePath$PathEnvDelimiter$env:PSModulePath" -} -if( -not (Test-Path $MyPSModulePath) ) { - New-Item -ItemType Directory -Path $MyPSModulePath -Force | Out-Null -} - -Write-Verbose $(Get-Item $MyPSModulePath | Select-Object -ExpandProperty FullName) - -# Establish local/personal Script Root and make sure it's in $env:Path -$local:p = Split-PathEnv $env:PATH -$MyPSScriptRoot = Join-Path (Split-Path -Parent $MyPSModulePath) Scripts -Write-Verbose "MyPSScriptRoot = $MyPSScriptRoot" -if( -not (Test-Path $MyPSScriptRoot) ) { - New-Item -ItemType Directory -Path $MyPSScriptRoot -Force | Out-Null - New-Item -ItemType Directory -Path $MyPSScriptRoot/profile.d -Force | Out-Null -} -$global:MyPSScriptRoot = $MyPSScriptRoot - -$p = @($p[0], $MyPSScriptRoot) + $($p | Select-Object -Skip 1) -$env:PATH = $p -join $PathEnvDelimiter -} ####################################################################### ## Setup-Profile code continues below ####################################################################### -if ( -not $MyPSScriptRoot -or (Test-Path function:Test-IsAdmin,function:ConvertTo-Base64,function:Invoke-ExpressionEx,function:Get-PowerShellPath,function:Export-FunctionSource | Where-Object { -not $_ } | Measure-Object | Select-Object -ExpandProperty Count) ) { - Write-Verbose "Calling ProfileCode inline..." - . ProfileCode_common -} - if ( $GitClone -and -not (Test-Path $(Join-Path $MyPSScriptRoot '.git')) ) { if( -not [bool]$(Get-Command git -ErrorAction SilentlyContinue) ) { throw "No git command found, you may either omit the -GitClone switch or install git and try again." @@ -187,15 +49,6 @@ if ( $GitClone -and -not (Test-Path $(Join-Path $MyPSScriptRoot '.git')) ) { } } -# if( $sudo -and -not $(Test-IsAdmin) ) { -# $local:sudo_args = @('-MyPSScriptRoot',$MyPSScriptRoot) -# $ScriptPSBoundParams.Keys | Where-Object { -# ($ScriptPSBoundParams[$_] -is [Switch]) -and $ScriptPSBoundParams[$_] -and ($_ -notin ('sudo','GitClone','GitURL')) -# } | ForEach-Object { $sudo_args += "-$_" } -# -# return Invoke-ExpressionEx -sudo -Function 'Setup-ProfileInternal' $sudo_args -# } - $local:ProfileSignature = [PSCustomObject]([ordered]@{ Begin = '#### SZ Auto Profile Setup - BEGIN ####' End = '#### SZ Auto Profile Setup - END ####' @@ -212,14 +65,13 @@ param($Name,$Path,$Status,$Exist) } $local:_profiles= $PROFILE | fl * -Force | Out-String -Stream | ForEach-Object { - $local:p = $($_ -split '( : /)|(:\\)'); + $local:p = $($_ -split ' : [/A-Z\\]'); if( $p[0] -match 'User' ) { $p[0].Trim() } } $local:_profile = $profile.CurrentUserAllHosts; $local:randomSeed = "####$(Get-Random)####"; -$local:written = $false; if( $RemoveOnly -and $ConfirmPreference -eq 'High' -and (-not $PSBoundParameters -or -not $PSBoundParameters.ContainsKey('Confirm')) ) { $script:ConfirmPreference = 'Low' @@ -284,6 +136,8 @@ $_profiles | Foreach-Object { Invoke-ExpressionEx -sudo:$shouldSudo chmod 'a+r' $tmpOutput.FullName } try { + function BreakPointHere{} BreakPointHere; + $local:errMsg = $(Invoke-ExpressionEx -sudo:$shouldSudo "Move-Item $($tmpOutput.FullName) $p -Force:`$$Force -ErrorAction Stop | Out-Null" 2>&1) if( $errMsg ) { Write-Error "$errMsg" @@ -321,8 +175,210 @@ $_profiles | Foreach-Object { Remove-Item function:ProfileCode* -Confirm:$false } +function ProfileCode_common { + function Get-PowerShellPath { + Get-Process -PID $PID | ForEach-Object { $_.Path,$_.Parent.Path } | Where-Object { $_ -match 'powershell|pwsh' } | Select-Object -First 1 + } + + function ConvertTo-Base64 { + param([string]$String) + return [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($String)) + } + + function Invoke-ExpressionEx { + [CmdletBinding(SupportsShouldProcess)]param( + [switch]$sudo, + [switch]$Force, + [string]$Function, + [switch]$MarshalOutput, + [Parameter(Position = 1, ValueFromRemainingArguments = $true)] + [string[]]$expr + ) + + function do_sudo { + [CmdletBinding()]param ( + [string]$sudo_cmd + ) + + function do_sudo_win { + [CmdletBinding()]param ( + [string]$sudo_cmd + ) + + $local:sudo_exec = [string]::Empty + if( (Get-Command scoop -ErrorAction SilentlyContinue) ) { + $sudo_exec = $(scoop which gsudo *>&1) + if( $sudo_exec -match 'not found' ) { + $sudo_exec = $(scoop which sudo *>&1) + } + if( $sudo_exec -match 'not found' ) { $sudo_exec = [string]::Empty } + } + if( $sudo_exec ) { + & $sudo_exec $sudo_cmd + } else { + Write-Error "Didn't find a known sudo command" + } + } + + if( $PSVersionTable.Platform -eq 'Unix' ) { + /usr/bin/env sudo $(Get-PowerShellPath) "-noprofile" "-EncodedCommand" $(ConvertTo-Base64 $sudo_cmd) + } else { + $local:currentIdenity = [System.Security.Principal.WindowsIdentity]::GetCurrent() + if( $currentIdenity.UserClaims.Value -contains 'S-1-5-32-544' ) { + do_sudo_win $sudo_cmd + } else { + Write-Error "User [$($currentIdenity.Name)]does not have permissions to run as admin." + } + } + + } + + if( $sudo -and -not $(Test-IsAdmin) ) { + $local:tmpOutputFile = $null + $local:tmpScriptFile = $null + + $local:sudo_cmd = "$($expr -join ' ')".Trim() + if( $Function ) { + $tmpScriptFile = New-TemporaryFile -WhatIf:$false -Confirm:$false + $tmpScriptFile = Move-Item $tmpScriptFile.FullName "$($tmpScriptFile.FullName).ps1" -PassThru -WhatIf:$false -Confirm:$false + + $local:tmpScriptContent = Export-FunctionSource $Function + $sudo_cmd = "$Function $sudo_cmd" + + $tmpScriptContent | Out-File $tmpScriptFile.FullName -WhatIf:$false + $expr = @( ".", $tmpScriptFile.FullName, ";", "`n$Function" ) + $expr + } + + $expr = @( + "`n`$WhatIfPreference=`$$WhatIfPreference;", + "`n`$VerbosePreference='$VerbosePreference';", + "`n`$ErrorActionPreference='$ErrorActionPreference';", + "`n`$ConfirmPreference='$ConfirmPreference';`n" + ) + $expr + + if( $MarshalOutput ) { + $tmpOutputFile = New-TemporaryFile -WhatIf:$false -Confirm:$false + $expr += @( "| Export-CliXml -Path '$($tmpOutputFile.FullName)'" ) + } + + $expr += @( ";`n exit `$LastExitCode" ) + + ## $tmpScriptContent += "$($expr -join ' ')".Trim() + Write-Verbose "Performing the following command line via SUDO:`n{$sudo_cmd}" + + $sudo_cmd = "$($expr -join ' ')".Trim() + + do_sudo $sudo_cmd + ## do_sudo 'Get-Content' $tmpScriptFile.FullName '|' 'Invoke-Expression' '|' 'Export-Clixml' '-Path' $tmpOutputFile.FullName + #do_sudo $sudo_cmd + + if( $tmpScriptFile ) { Remove-Item $tmpScriptFile } + if( $tmpOutputFile ) { Import-Clixml $tmpOutputFile; Remove-Item $tmpOutputFile } + return + } + + Write-Verbose "Perofrming the following expression in-line:`n{$($expr -join ' ')}" + Invoke-Expression "$expr" + } + + function Test-IsAdmin{ + [CmdletBinding(ConfirmImpact = 'None')] + [OutputType([bool])] + param () + + switch($true){ + $( ($PSVersionTable.PSEdition -eq 'Desktop') -or + ($PSVersionTable.Platform -eq 'Win32NT') + ) { + # Fastest way on Windows + ([Security.Principal.WindowsPrincipal]( + [Security.Principal.WindowsIdentity]::GetCurrent() + )).IsInRole( + [Security.Principal.WindowsBuiltInRole]'Administrator' + ) + } + $( ($PSVersionTable.PSEdition -eq 'Core') -and + ($PSVersionTable.Platform -eq 'Unix') + ) { + # On macOS and Linux we use ID to figure out if we run elevated (0 means superuser rights) + return $((id -u) -eq 0) + } + default { + # Unable to figure it out! + Write-Warning -Message 'Unknown' + + return + } + } + + } + + function Export-FunctionSource { + param( + [switch]$NoHeader, + [Parameter(Position = 0, ValueFromRemainingArguments = $true)] + #[ValidateSet([Functions])] + [string[]]$FunctionName + + ) + + $local:src = "" + foreach( $local:func in $FunctionName ) { + if( -not $NoHeader ) { $src += "`nfunction $func {" } + $src += "`n" + $src += $((Get-Command -Type Function $func).Definition) ### .Replace(('$'+'_'),('$$'+'_')) + if( -not $NoHeader ) { $src += "`n}" } + $src += "`n" + } + return $src.Trim() + } + + # Are we running in Unix or Windows? + $global:PathEnvDelimiter = $(if( $PSVersionTable.Platform -match 'unix' ) {':'} else {';'}) + function Split-PathEnv { + param([string]$EnvPath) + $EnvPath -split $PathEnvDelimiter + } + + # Establish Module Path + $global:MyPSModulePath = Split-PathEnv $env:PSModulePath | + Where-Object { $_ -match "^$($(Resolve-Path ~) -replace '\\',"\\")" } | + Where-Object { Test-Path $_ } | + Get-Item | Sort-Object -Property LastWriteTime -Descending | + Select-Object -ExpandProperty FullName -First 1 + + if( -not $MyPSModulePath ) { + $MyPSModulePath = $(Join-Path $(Join-Path $(Resolve-Path ~) 'powershell') 'Modules') + $env:PSModulePath = "$MyPSModulePath$PathEnvDelimiter$env:PSModulePath" + } + if( -not (Test-Path $MyPSModulePath) ) { + New-Item -ItemType Directory -Path $MyPSModulePath -Force | Out-Null + } + + Write-Verbose $(Get-Item $MyPSModulePath | Select-Object -ExpandProperty FullName) + + # Establish local/personal Script Root and make sure it's in $env:Path + $local:p = Split-PathEnv $env:PATH + $MyPSScriptRoot = Join-Path (Split-Path -Parent $MyPSModulePath) Scripts + Write-Verbose "MyPSScriptRoot = $MyPSScriptRoot" + if( -not (Test-Path $MyPSScriptRoot) ) { + New-Item -ItemType Directory -Path $MyPSScriptRoot -Force | Out-Null + New-Item -ItemType Directory -Path $MyPSScriptRoot/profile.d -Force | Out-Null + } + $global:MyPSScriptRoot = $MyPSScriptRoot + + $p = @($p[0], $MyPSScriptRoot) + $($p | Select-Object -Skip 1) + $env:PATH = $p -join $PathEnvDelimiter +} + + +if ( -not $MyPSScriptRoot -or (Test-Path function:Test-IsAdmin,function:ConvertTo-Base64,function:Invoke-ExpressionEx,function:Get-PowerShellPath,function:Export-FunctionSource | Where-Object { -not $_ } | Measure-Object | Select-Object -ExpandProperty Count) ) { + Write-Verbose "Calling ProfileCode inline..." + . ProfileCode_common +} + if( -not $SetupFromWeb ) { _setup @PSBoundParameters Get-Item function:_setup | Remove-Item } -Remove-Variable SetupFromWeb +Remove-Variable SetupFromWeb -ErrorAction SilentlyContinue