... and if I understand @BAU correctly, for .ps1 scripts Code: #:: [info] to integrate in .ps1 files, add RunAsTI snippet and this line on bottom and wrap main code as $main = { code } if ((whoami)-ne"nt authority\system") {RunAsTI "powershell -file ""$($MyInvocation.MyCommand.Path)"" $args";return}; & $main $args (as an example running Start-Optimize.ps1 elevated as TI) Code: <# .SYNOPSIS Start-Optimize is a configuration call script for the Optimize-Offline module. .DESCRIPTION Start-Optimize automatically imports the configuration JSON file into the Optimize-Offline module. .EXAMPLE .\Start-Optimize.ps1 This command will import all values set in the configuration JSON file into the Optimize-Offline module and begin the optimization process. .NOTES Start-Optimize requires that the configuration JSON file is present in the root path of the Optimize-Offline module. #> $main = { [CmdletBinding()] Param ( [Parameter(Mandatory = $false)] [switch]$populateLists, [Parameter(Mandatory = $false)] [switch]$populateTemplates ) ... ... ... } Else { Try { Import-Module (Join-Path -Path $PSScriptRoot -ChildPath Optimize-Offline.psm1) -Force -WarningAction Ignore -ErrorAction Stop } Catch { Write-Warning ('Failed to import the Optimize-Offline module: "{0}"' -f (Join-Path -Path $PSScriptRoot -ChildPath Optimize-Offline.psm1)) Start-Sleep 3 Exit } } Optimize-Offline @ConfigParams } if ((whoami)-ne"nt authority\system") {RunAsTI "powershell -file ""$($MyInvocation.MyCommand.Path)"" $args";return}; & $main $args #:RunAsTI: #1 snippet to run as TI/System, with /high priority, /priv ownership, explorer and HKCU load set ^ #=& set "0=%~f0"& set 1=%*& powershell -nop -c iex(([io.file]::ReadAllText($env:0)-split':RunAsTI\:.*')[1])& exit/b ................................................. etc
Yeah, I'm sure it would. But the only issue I have is a few services error out and would be nice if a fix was integrated into the script.
That doesn't start optimize offline, it just opens an explorer window with all the privileges escalated so I can browse to the .bat or start PowerShell in the optimize offline folder. Wait, I edited it wrong, but get this error now. Code: C:\Windows\system32>powershell .\Start-Optimize .\Start-Optimize : The term '.\Start-Optimize' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. At line:1 char:1 + .\Start-Optimize + ~~~~~~~~~~~~~~~~ + CategoryInfo : ObjectNotFound: (.\Start-Optimize:String) [], CommandNotFoundException + FullyQualifiedErrorId : CommandNotFoundException C:\Windows\system32>#:RunAsTI: #1 snippet to run as TI/System, with /high priority, /priv ownership, explorer and HKCU load The system cannot find the drive specified. C:\Windows\system32>set #= & set "0=D:\Optimize-Offline-master\Optimize-Offline-master\Start-Optimize.bat" & set 1= & powershell -nop -c iex(([io.file]::ReadAllText($env:0)-split':RunAsTI\:.*')[1]) & exit/b
This works though. Code: whoami|findstr /i /c:"nt authority\system" >nul || ( call :RunAsTI "%~f0" %* & exit/b ) powershell "D:\Optimize-Offline-master\Optimize-Offline-master\Start-Optimize.ps1" #:RunAsTI: #1 snippet to run as TI/System, with /high priority, /priv ownership, explorer and HKCU load etc. it bothers my OCD I get this error though even though the script finishes just fine. Code: The value Ignore is not supported for an ActionPreference variable. The provided value should be used only as a value for a preference parameter, and has been replaced by the default value. For more information, see the Help topic, "about_Preference_Variables." At D:\Optimize-Offline-master\Optimize-Offline-master\Src\Public\Remove-Container.ps1:22 char:49 + ... th $Item) { Remove-Item -LiteralPath $Item -Recurse -Force -ErrorActi ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : OperationStopped: (:) [], NotSupportedException + FullyQualifiedErrorId : System.NotSupportedException Exporting media from "19044.1320.211013-2035.21H2_RELEASE_SVC_PROD3_CLIENT_X64FRE_EN-US.iso" Supported Image Build: [19044] Mounting Windows 10 Enterprise
Start-Optimize.ps1 : Code: <# .SYNOPSIS Start-Optimize is a configuration call script for the Optimize-Offline module. .DESCRIPTION Start-Optimize automatically imports the configuration JSON file into the Optimize-Offline module. .EXAMPLE .\Start-Optimize.ps1 This command will import all values set in the configuration JSON file into the Optimize-Offline module and begin the optimization process. .NOTES Start-Optimize requires that the configuration JSON file is present in the root path of the Optimize-Offline module. #> [CmdletBinding()] Param ( [Parameter(Mandatory = $false)] [switch]$populateLists, [Parameter(Mandatory = $false)] [switch]$populateTemplates ) $Global:Error.Clear() # Ensure we are running with full administrative permissions via RunAsTI function. function RunAsTI ($cmd) { $id='RunAsTI'; $sid=((whoami /user)-split' ')[-1]; $code=@' $ti=(whoami /groups)-like"*1-16-16384*"; $DM=[AppDomain]::CurrentDomain."DefineDynamicAss`embly"(1,1)."DefineDynamicMod`ule"(1) $D=@(); 0..5|% {$D+=$DM."DefineT`ype"("M$_",1179913,[ValueType])}; $I=[int32];$P=$I.module.gettype("System.Int`Ptr"); $U=[uintptr] $D+=$U; 4..6|% {$D+=$D[$_]."MakeB`yRefType"()};$M=$I.module.gettype("System.Runtime.Interop`Services.Mar`shal");$Z=[uintptr]::size $S=[string]; $F="kernel","advapi","advapi",($S,$S,$I,$I,$I,$I,$I,$S,$D[7],$D[8]),($U,$S,$I,$I,$D[9]),($U,$S,$I,$I,[byte[]],$I) 0..2|% {$9=$D[0]."DefinePInvokeMeth`od"(("CreateProcess","RegOpenKeyEx","RegSetValueEx")[$_],$F[$_]+'32',8214,1,$S,$F[$_+3],1,4)} $DF=0,($P,$I,$P),($I,$I,$I,$I,$P,$D[1]),($I,$S,$S,$S,$I,$I,$I,$I,$I,$I,$I,$I,[int16],[int16],$P,$P,$P,$P),($D[3],$P),($P,$P,$I,$I) 1..5|% {$k=$_;$n=1;$AveYo=1; $DF[$_]|% {$9=$D[$k]."DefineFie`ld"('f'+$n++,$_,6)}}; $T=@(); 0..5|% {$T+=$D[$_]."CreateT`ype"()} 0..5|% {nv "A$_" ([Activator]::CreateInstance($T[$_])) -force}; function F ($1,$2) {$T[0]."GetMeth`od"($1).invoke(0,$2)}; if (!$ti) { $g=0; "TrustedInstaller","lsass"|% {if (!$g) {net1 start $_ 2>&1 >$null; $g=@(get-process -name $_ -ea 0|% {$_})[0]}} function M($1,$2,$3){$M."GetMeth`od"($1,[type[]]$2).invoke(0,$3)}; $H=@(); $Z,(4*$Z+16)|% {$H+=M "AllocHG`lobal" $I $_}; M "WriteInt`Ptr" ($P,$P) ($H[0],$g.Handle); $A1.f1=131072;$A1.f2=$Z;$A1.f3=$H[0];$A2.f1=1;$A2.f2=1;$A2.f3=1;$A2.f4=1;$A2.f6=$A1 $A3.f1=10*$Z+32;$A4.f1=$A3;$A4.f2=$H[1]; M "StructureTo`Ptr" ($D[2],$P,[boolean]) (($A2 -as $D[2]),$A4.f2,$false); $w=0x0E080600 $out=@($null,"powershell -win 1 -nop -c iex `$env:A",0,0,0,$w,0,$null,($A4 -as $T[4]),($A5 -as $T[5])); F "CreateProcess" $out } else { $env:A=''; $PRIV=[uri].module.gettype("System.Diagnostics.Process")."GetMeth`ods"(42) |? {$_.Name -eq "SetPrivilege"} "SeSecurityPrivilege","SeTakeOwnershipPrivilege","SeBackupPrivilege","SeRestorePrivilege" |% {$PRIV.Invoke(0, @("$_",2))} $HKU=[uintptr][uint32]2147483651; $LNK=$HKU; $reg=@($HKU,"S-1-5-18",8,2,($LNK -as $D[9])); F "RegOpenKeyEx" $reg; $LNK=$reg[4] function SYM($1,$2){$b=[Text.Encoding]::Unicode.GetBytes("\Registry\User\$1");@($2,"SymbolicLinkValue",0,6,[byte[]]$b,$b.Length)} if (!$cmd) {$cmd='cmd'}; $r="start `"$id`" /high /w"; F "RegSetValueEx" (SYM $(($key-split'\\')[1]) $LNK) start cmd -args ("/q/x/d/r title $id && $r",$cmd) -wait -win 1; F "RegSetValueEx" (SYM ".Default" $LNK) } # lean and mean snippet by AveYo, 2018-2021 '@; $key="Registry::HKEY_USERS\$sid\Volatile Environment"; $a1="`$id='$id';`$key='$key';";$a2="`$cmd='$($cmd-replace"'","''")';`n" sp $key $id $($a1,$a2,$code) -type 7 -force; $arg="$a1 `$env:A=(gi `$key).getvalue(`$id)-join'';rp `$key `$id -force; iex `$env:A" start powershell -args "-win 1 -nop -c $arg" -verb runas } $arguments = @() foreach ($param in $PSBoundParameters.GetEnumerator()) { $arguments += "-"+[string]$param.Key+$(If ($param.Value -notin @("True", "False")) {"="+$param.Value} Else {""}) } if ((whoami)-ne"nt authority\system") {RunAsTI "powershell -c . ""$($MyInvocation.MyCommand.Path)"" $arguments; pause"; return} # Ensure the configuration JSON file exists. If (!(Test-Path -Path (Join-Path -Path $PSScriptRoot -ChildPath Configuration.json))) { Write-Warning ('The required configuration JSON file does not exist: "{0}"' -f (Join-Path -Path $PSScriptRoot -ChildPath Configuration.json)) Start-Sleep 3 Exit } # If the configuration JSON or ordered collection list variables still exists from a previous session, remove them. If ((Test-Path -Path Variable:\ContentJSON) -or (Test-Path -Path Variable:\ConfigParams)) { Remove-Variable -Name ContentJSON, ConfigParams -ErrorAction Ignore } # Use a Try/Catch/Finally block in case the configuration JSON file URL formatting is invalid so we can catch it, correct its formatting and continue. Try { $ContentJSON = Get-Content -Path (Join-Path -Path $PSScriptRoot -ChildPath Configuration.json) -Raw | ConvertFrom-Json } Catch [ArgumentException] { $ContentJSON = (Get-Content -Path (Join-Path -Path $PSScriptRoot -ChildPath Configuration.json) -Raw).Replace('\', '\\') | Set-Content -Path (Join-Path -Path $Env:TEMP -ChildPath Configuration.json) -Encoding UTF8 -Force -PassThru $ContentJSON = $ContentJSON | ConvertFrom-Json Move-Item -Path (Join-Path -Path $Env:TEMP -ChildPath Configuration.json) -Destination $PSScriptRoot -Force $Global:Error.Remove($Error[-1]) } Finally { $ContentJSON.PSObject.Properties.Remove('_Info') } # Convert the JSON object into a nested ordered collection list. We use the PSObject.Properties method to retain the JSON object order. $ConfigParams = [Ordered]@{ populateLists = $populateLists populateTemplates = $populateTemplates } ForEach ($Name In $ContentJSON.PSObject.Properties.Name) { $Value = $ContentJSON.PSObject.Properties.Item($Name).Value If ($Value -is [PSCustomObject]) { $ConfigParams.$Name = [Ordered]@{ } ForEach ($Property in $Value.PSObject.Properties) { $ConfigParams.$Name[$Property.Name] = $Property.Value } } Else { $ConfigParams.$Name = $Value } } # Import the Optimize-Offline module and call it by passing the JSON configuration. If ($PSVersionTable.PSVersion.Major -gt 5) { Try { Import-Module Dism -SkipEditionCheck -Force -WarningAction Ignore -ErrorAction Stop } Catch { Write-Warning 'Failed to import the required Dism module.' Start-Sleep 3 Exit } Try { Import-Module (Join-Path -Path $PSScriptRoot -ChildPath Optimize-Offline.psm1) -SkipEditionCheck -Force -WarningAction Ignore -ErrorAction Stop } Catch { Write-Warning ('Failed to import the Optimize-Offline module: "{0}"' -f (Join-Path -Path $PSScriptRoot -ChildPath Optimize-Offline.psm1)) Start-Sleep 3 Exit } } Else { Try { Import-Module (Join-Path -Path $PSScriptRoot -ChildPath Optimize-Offline.psm1) -Force -WarningAction Ignore -ErrorAction Stop } Catch { Write-Warning ('Failed to import the Optimize-Offline module: "{0}"' -f (Join-Path -Path $PSScriptRoot -ChildPath Optimize-Offline.psm1)) Start-Sleep 3 Exit } } Optimize-Offline @ConfigParams The generic RunAsTI example uses the (simpler) $args automatic variable, while Start-Optimize.ps1 and other scripts in this project use the (bloated) Param () But that does not mean you can't implement the function into the script, as shown above! Great idea btw, since RunAsTI not only uses TrustedInstaller, but also pre-enables privileges and loads the current user registry hive (no other TI/System solutions do this, so you end up with the system hive in HKCU) If you still get permission denied even after RunAsTI, then something must be wrong or you're doing something wrong Back to the script, I have a question: why does it copy the source image to the script folder to do work on it, instead of directly doing work on it without this extra copy (if the source image is writable, of course)? That's not very optimal. Script should assume owner already has a safe copy (in an iso - probably).
Further testing I found the errors in my case was a poor "Advanced Services" entry. Corrected & working fine for now.
There is an old engineering saying that relates to trusting in the general competence of the average user: When you ass-u-me you make an 'ass' out of 'u' and 'me'. Thanks very much for the script and even more for your deeper insights into it's features/benefits! If you don't mind. I'll chuck your script onto GitHub for @gdeliana to consider incorporating onto the project
Tho in this case I'm fairly convinced any user, average or not, will prefer not to wait for a copy of 4-6 GB install.wim needlessly trashing the storage. Again, the original should already be inside some iso file Yes, there are real benefits doing anything involving dism with the extra privileges, because dism has always been kinda' broken when it comes to mounting images, it might apply real ownership and permissions (that the current user / admins do not have) to the mounted files, instead of keeping all the ACL's separately cached.
Hi @BAU thanks for your great script. it works really well! I have a few questions about some of your MDL contributions on different threads. I hope you don't mind if I put them all here since they all fall under the "Optimize" topic heading. (1) "HIVE loading" Does launching the optimize script as TrustedInstaller automatically force any hive opening code to load the current user registry hive or does the script need code changes to make sure that happens? (2) Is your Defender toggle still the best solution for Windows 11? Has your opinion of Windows 11 changed? [edit] I had written more here, but it got accidently xinso'd (3) "EDGE removal" I think you've seen the "Proprietary" Microsoft Edge Configurator I prefer your https://forums.mydigitallife.net/threads/microsoft-edge.79237/page-137#post-1630576 Edge removal method, but I'm not sure if it needs updating or how to call it as a script? (You're instructions are to paste it into an PowerShell (Admin) console)
You could ask these questions (publicly) in my snippets repository, as to not pollute this thread any more than it already is Spoiler: Anyway, (1) a. For the duration of the script, the main process and any child processes launched by it, will have HKCU as a proper USER one, not the SYSTEM one. This method (basically manipulating soft-links for reg hives) is invented by me so no other solutions have it, or ever attempted such an useful enhancement that apply outside the calling process (for which ms offers winapi functions). So you end up with the power of TrustedInstaller minus the hassle of checking your registry tweaks to not have HKCU entries or using reg load / reg unload with renamed HKCU entries and it helps with any external tools launched that expect most if not all keys to be present (in HKCU) but do not find them because running as TI loads the TI (system) registry. (1) b. The explorer load is not under the scope of this project since you only need launching powershell, so I've cut it out to keep the code shorter. It would be needed if wanting to open explorer and do file-management stuff with full rights, as explorer never launches directly in newer windows, but via a host process (sihost.exe) and after few UAC blocks. That's not so hard to overcome, the problem is that the explorer.exe process does not exit after you close the window, but lingers on for up to 30-45 seconds, so in the context of modified HKCU, script needs to wait for explorer.exe to exit and then revert the changes. This does happen automatically and out-of-sight (figured adding a running as ti;done/cancel gui for it would be unnecessary annoying). Note that the HKCU modification goes away after reboot anyway (in case something would go wrong). (2) a. Yes, I'm going to say use ToggleDefender, because complete removal of built-in security software is such a bad idea, just to gain 150ish MB of space (probably less now). When it comes to 11, the bad idea can turn into stupid idea, since windows connects to the network during setup and you might get hit by the next EthernalBlue even before you complete it. That's an experimental feature and we already know ms is incompetent... You have another security solution? That's great. Only that on upgrade setup will often nuke 3rd party AV, leaving you butt-naked to threats if it can't restore defender because you've chopped it off. I'm tired of explaining why complete removal alongside with the network protection features is bad. Just disabling it (with a bit of DenialOfService magic) is simply enough! No real time scanning turning itself back on, no routine weekly/monthly "wake-up" despite having another AV installed, and no updates. (2) b. ToggleDefender has worked just fine in 11 from the get-go, which I cannot say about other popular solutions - that imho, should not be used because of multiple reasons, starting with the fact that are 3rd party binaries that should never get near your security solutions - any os tweaking, really, should only be distributed in plain-text scripts or source code, and only involve 3rd party binaries if absolutely needed. Anything else, from dumb bat2exe to other complicated / obfuscated executables is just developers more interested into "protecting" their often garbage pack (usually 99.9% community made reg tweaks they did not come up with) and leaving the door open to sneak in other s**t. Plus, in the era of AV's getting dumber than what we had in year2k, aka walled garden that blocks everything not on a proprietary list, indiscriminately, binaries are a nuisance for end users, and so are the hosting sites for them. (2) c. I'm gonna have to revise that 11 review, because it's either microsoft purposely making 10 suck more to make 11 look better, or 11 is getting better performance-wise (after you get rid of widgets/teams/edge/new startmenu garbage). Even with my 3770k I can feel a performance boost in multi-tasking and even storage-performance while working with virtual machines and other tasks. So it can't be just scheduler and inter-core communication changes because my potatoes don't even know how to take advantage of that. Retpoline's could have been optimized, but why would microsoft do that for older cpus that are deemed unsupported? I'm worried some of those cpu vulnerabilities safeguards have been lifted after assuming the cpu has fixes.. But that's easy to test, since you can revert to an older 10 LCU just by in-place upgrade to 20H1 / 20H2 / 21H1 esd's (3) a. I've seen the preview image and figured it is a script, did not know it's a "bat2exe" that bundles reg twaks and edge setup binaries (why?). Unliked it, because (2) b. I actually use that script as is quite often with all sort of windows vm's, but I also use the force appx uninstall thingy (configured for my needs). I will eventually get to it and see if it can be improved, then add it to my scripts repository. But that's for online tweaking, while this project is about more efficient result offline optimization (at the cost of more processing). If you're on a 32GB emmc / slow storage / cpu or something like that, pre-removal has it's benefits. What you could do is implement the bits that are usable to prevent the os reinstalling the removed apps (EndOfLife trick). Might help with that later on.
I keep getting the following error when trying to populate the lists, right after selecting the edition. "Log : The term 'Write-Log' is not recognized as the name of a cmdlet.... At gdeliana-2\Optimize-Offline.psm1:2558 char:3"
I'm not 100% sure of the answer, since I've never had that error. Line 2558 in "Optimize-Offline.psm1" is "Log "Clearing temp directory..."" Things that help with temp directory issues (which may not fix your problem, but they're good things to do anyway). (1) Extract and run the OO script package on a short path e.g. C:\OO<package contents> (2) Once the script starts running, close the explorer window to the "C:\OO\" folder. If you want to be 100% sure, than shut down all explorer windows until the OO script completes. See https://forums.mydigitallife.net/th...20h1-and-ltsc-2019.80038/page-71#post-1698520(3) use BAU's Start-Optimize.ps1
Just tried to run the full thing. Says it failed to remove the provisioned packages. I did notice it said it was the wrong "supported version" 19041 instead of 19044. Maybe an issue with the updates I integrated with W10UI?
could anyone with a lot of knowledge care to explain to me about debloater. or debloating windows? fi you want you cam PM me. its been like 2 years since i used my laptop and will have it ready ready to use in like 2-3 weeks so am doing some research first. i used msmg toolkit to create a custom iso and removed components but at that time updatin windows would bring back removed components. so i was thinking i should just update and debloat windows. here is where i have questions. why are there so many debloaters around.? why should i pick this over the rest.? so removing components i have to run debloater each time right.?
I suggest you to read first about this debloating script in detail & test it in virtual environment/non-primary pc for a few days/week to confirm that everything is working fine for you as everyone's usage scenario is different & what works for someone may not work for another. I am using 21H1 Education with some minor tweaks only(like disabling automatic driver updates, pausing win updates for 30 days, disabling some unnecessary services like xbox which I don't have etc) & everything works just fine after I forget about MS store icon in taskbar. Ram usage is bit more than LTSC I was using earlier but not significant difference with my 8gb ram & if I had 16gb ram I think it would not have been noticeable at all.
Because Windows is a bloated mess?! Very simply. You're timing is very good. For windows 10, you don't need to mess around with any offline or online deblaoting script. If you want windows 10, then focus your learning on the client build of Win10 LTSC 2021 which is 99% likily to get officially released this month or in very early December and comes pre-deblaoted right out of the box. There are two flavors of LTSC. One is expected to be KMS licensed with 5 years of support and the other, called LTSC IoT, is HWID licensed and has ten years of support. In the past, there were differences between the client versions of LTSC and LTSC IoT, but this time they are essentially identical. There are many confused people talking about this upcoming release so it's easy to get confused by them. What I just told you is all you really need to know. If you want to learn a bit more you can look at this thread: [Chit Chat] Windows 10 Enterprise/IoT Enterprise (N) LTSC 2021. If you do go to that thread, skip over the pages and pages of clueless idiots posting on it and read the ten or twelve posts by abbodi1406, BAU and Enthousiast. They'll basically repeat the same things I just said, but sometimes it's good to hear the same thing repeated over and over by different people in different ways and then it all makes perfect sense. Sure, using this optimize-offline script will lighten-up even a LTSC client OS, but it's not really going to make that much of a difference. There might be a reason that you have to use the retail version of Windows 10 or you may want Windows 11. If you want Windows 11, on an old laptop, then you've got to do more research and test installs, etc. I can't really help you with any of that. For debloating Windows 11 or a retail, non-LTSC, Windows 10 then please follow the very good advice you got from @whitestar_999.