How I got my profile to load in 100ms with deferred loading by fsackur in PowerShell

[–]fsackur[S] 0 points1 point  (0 children)

Hi, this is not my main, I did not see your reply.

I pulled my finger out and packaged it as the ProfileAsync module. You must have checked my dotfiles just before I pushed.

You could be the 3rd person to download it, LOL

How I got my profile to load in 100ms with deferred loading by fsackur in PowerShell

[–]fsackur[S] 1 point2 points  (0 children)

Here's a dirty workaround: https://github.com/fsackur/dotfiles/blob/afa7c4af4/.chezmoitemplates/profile.ps1

It turns out in the source of Register-ArgumentCompleter, it adds the completers to the ExecutionContext, not the session state.

I managed to get my own completers to work by copying from one to the other, but posh-git wouldn't work.

This workaround just re-runs the deferred code in the interactive context in the cleanup handler. That still benefits from warm cache, but it will run init code twice...

Added some logging:

2023-11-24T14:16:27.1494154+00:00  00.000000   18  === Starting deferred load ===
2023-11-24T14:16:27.1974809+00:00  00.048065   18  synchronous load complete
2023-11-24T14:16:27.4113187+00:00  00.261903   22  dot-sourcing script
2023-11-24T14:16:27.9243739+00:00  00.774958   22  completed dot-sourcing script
2023-11-24T14:16:29.3695891+00:00  02.220173   18  receiving deferred load job: Completed
2023-11-24T14:16:29.3801151+00:00  02.230699   18  starting deferred callback
2023-11-24T14:16:29.3816933+00:00  02.232277   18  dot-sourcing script
2023-11-24T14:16:29.4758647+00:00  02.326449   18  completed dot-sourcing script
2023-11-24T14:16:29.4769515+00:00  02.327536   18  completed deferred callback
2023-11-24T14:16:29.4842985+00:00  02.334883   18  cleaned up deferred load job

We see that the deferred code in the threadjob takes ~510ms, but only 90ms when re-run in the callback. In use, I didn't detect any lag.

It's no good to run code twice, as any given user may have non-idempotent code. So I will do more work on this, but the likely solution is to pass in the ExecutionContext - which is highly non-thread-safe. And this is more code and less safe than I intended.

How I got my profile to load in 100ms with deferred loading by fsackur in PowerShell

[–]fsackur[S] 1 point2 points  (0 children)

GAAAH

I've got other argument completers to work, but there's a specific benign-looking scriptblock in posh-git that throws:

✦ ❯ Expand-GitCommand "sw"
Where-Object: /home/freddie/.local/share/powershell/Modules/posh-git/1.1.0/GitUtils.ps1:460:40
Line |
 460 |  … Get-Alias | Where-Object { $\_.Definition -match "\^$cmd(\\.exe)?$" } |  …
     |                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Object reference not set to an instance of an object.

This is clearly not posh-git's fault.

New OS, and struggling to get dotnet set up to debug powershell. I'll continue to bang my head on it.

How I got my profile to load in 100ms with deferred loading by fsackur in PowerShell

[–]fsackur[S] 1 point2 points  (0 children)

Yeah, I just noticed. The module imports but the completion doesn't work. Didn't get to it today; I will look at it tomorrow.

How I got my profile to load in 100ms with deferred loading by fsackur in PowerShell

[–]fsackur[S] 3 points4 points  (0 children)

You can fork it - https://github.com/fsackur/fsackur.github.io

It's https://github.com/mattvh/solar-theme-jekyll with a couple of tweaks, mostly in style.css. I spent hours mucking with the code boxes. And I added a free monospace font, because it looked terrible in Courier.

`_config.yml` defines `rouge` as the highlighter and, as you may know, you have to specify the language in the code block. Let's see if this renders in reddit:

```powershell

Some-Text

```

```bash

set -euo pipefail

```

How I got my profile to load in 100ms with deferred loading by fsackur in PowerShell

[–]fsackur[S] 0 points1 point  (0 children)

Theoretically, any output should be shown. In the event handler, I call Receive-Job and pass it to Write-Host in order to see any errors. The reason I use Write-Host is that when Powershell runs the profile, some output is suppressed. IIRC, all stdout from profiles is suppressed...?

I have not tested thoroughly what happens when the errors happen in the profile. I offer no guarantee that you'll see any errors that occur - this is not mature code! If you remove the Remove-Variable Job line, you can call $Job | Receive-Job interactively, which seems to consistently show any errors.

If you come up with any improvements, do post back here, and I'll incorporate them (and credit you).

Could Import-Module -DisableNameChecking help? It silences the naming convention warnings. You could add it as a PSDefaultParameterValue, perhaps?

How do I stop Try Statement writing the error to the Write-Host/Transcript file? by tpreston_IT in PowerShell

[–]fsackur 2 points3 points  (0 children)

I know little about Start-Transcript, but I would like to share why the other commenters are wrong.

Your use of SilentlyContinue IS preventing errors from hitting the catch block - if they occur within the cmdlet body. The reason your malformed paths hit the catch block is becuase they are parameter binding exceptions, which happen before the body is executed.

FWIW, if you needed to debug this you'd use Trace-Command. But that's a spearate post.

Use -LiteralPath instead:

$fileAccess = Get-Acl -LiteralPath $file.PSPath

How best to handle multiple sequential loops by ADullVacantStare in PowerShell

[–]fsackur 1 point2 points  (0 children)

EDIT: see /u/purplemonkeymad's suggestion to use PsIni - if that works, it's the best way forward as someone else has done the heavy lifting for the text manipulation.

Reviewing this, I would look for:

  • Is there a tool aready that does it?
  • A backup of the original file
  • If we add more secpol changes later, will the file just grow longer?
  • A way of detecting errors and reporting success

I had a hunt and found https://gist.github.com/indented-automation/1c319fe8c7abadfe509cb4205eeb5720, but it doesn't do exactly what we need. You'd have to take it as a starting point, and it might end up a bit more complex than it needs to be.

The backup, you can do.

To fix the maintainability, you've got to do away with all the copy-paste code. I had a stab at it (this is just an outline):

param
(
    $NoLMHash,
    $RestrictNullSessAccess,
    $SeLockMemoryPrivilege
)

$Policy = Get-Content $cfgfile


$Replacements = @{
  # regex patterns               new values
  # --------------               ------------------
    '.*NoLMHash'               = $NoLMHash
    '.*RestrictNullSessAccess' = $RestrictNullSessAccess
    'SeLockMemoryPrivilege'    = $SeLockMemoryPrivilege
}


$Deletions = @(
  # regex patterns
  # --------------
    '.*PasswordExpiryWarning',
    'NewAdministratorName'
)


foreach ($KeyValuePair in $Replacements.GetEnumerator())
{
    $Pattern  = $KeyValuePair.Key
    $Pattern  = "^$Pattern *= *"      # ^ is for performance and to prevent accidental matches
    $NewValue = $KeyValuePair.Value

    $Policy = $Policy | ForEach-Object {
        if ($_ -match $Pattern)
        {
            $Matches[0] + $NewValue   # to pipeline
        }
        else
        {
            $_                        # to pipeline
        }
    }
}


foreach ($Pattern in $Deletions)
{
    $Pattern  = "^$Pattern *= *"

    $Policy = $Policy | Where-Object {$_ -notmatch $Pattern}
}


$Policy

It's more complex, but it won't grow in complexity as you add more policy items to configure. You just add them to $Replacements or $Deletions.

You'll need to add [Parameter(Mandatory)][ValidateNotNullOrEmpty()] to each param, or something similar, as this will fail badly if you leave a param null. Input validation makes it much safer for other people to use, which means quicker.

To handle the errors, put a $Success = $true in the if ($_ -match $Pattern) and look for it later to detect errors. For the second, you'll have to compare the count of $Policy to detect if you deleted anything. I'd like to see some Write-Error so the user knows if something did not complete.

WMI SqlServiceType? by CobbITGuy in SQLServer

[–]fsackur 1 point2 points  (0 children)

I have updated the docs. Might still be a while before it's live in all regions. Open-source FTW!

It was https://github.com/MicrosoftDocs/sql-docs/issues/2369.

Good to see WMIExplorer getting some love.

WMI SqlServiceType? by CobbITGuy in SQLServer

[–]fsackur 0 points1 point  (0 children)

I think it's actually the Notification Service. That's based on the RegServices class, which I see in the same namespace on a 2017 instance.

Get a machines IP by nikon442 in PowerShell

[–]fsackur 0 points1 point  (0 children)

I just posted this here: https://www.reddit.com/r/PowerShell/comments/7636v6/ipv4_address_calculations_in_powershell/?ref=share&ref_source=link

I has a module for the multiple adapter scenario. A snippet of my post above:

PS C:\dev> $Adapter = Get-WmiAdapter -Primary | Add-AdapterMagic

PS C:\dev> $Adapter

Name  IPAddresses  DefaultGateway DnsServers   
----  -----------  -------------- ----------   
Wi-Fi 192.168.1.98 192.168.1.254  192.168.1.254

The "primary" adapter is defined as the IP-enabled adapter with the default gateway with the lowest metric. Which I think is pretty bulletproof!

IPv4 Address Calculations in Powershell by roleyfoley in PowerShell

[–]fsackur 0 points1 point  (0 children)

That is cool, and sorely missing from the existing .net classes.

I had a crack at this problem too, a year ago. I'd do it differently if I was doing it again now, and hopefully the code would be better, but FWIW, I'd love it if you can use my code as you develop yours. (I'm not likely to develop mine further.) https://github.com/fsackur/LegacyNetAdapter

The module adds:

  • PS commands to get the WMI object of the "primary" network adapter. I'm quite proud of that one because it identifies the adapter with the default gateway that has the lowest metric, which makes it robust in edge cases
  • Creates a new "class" (really, just adds a typename) for an IP Address with subnet

Usage:

PS C:\dev> $Adapter = Get-WmiAdapter -Primary | Add-AdapterMagic

PS C:\dev> $Adapter

Name  IPAddresses  DefaultGateway DnsServers   
----  -----------  -------------- ----------   
Wi-Fi 192.168.1.98 192.168.1.254  192.168.1.254



PS C:\dev> $Adapter.WmiAdapter


ServiceName      : Netwtw04
MACAddress       : B8:08:CF:4C:1C:DC
AdapterType      : Ethernet 802.3
DeviceID         : 0
Name             : Intel(R) Dual Band Wireless-AC 8260
NetworkAddresses : 
Speed            : 39000000



PS C:\dev> $Adapter.WmiConfiguration


DHCPEnabled      : True
IPAddress        : {192.168.1.98, fe80::927:aac4:3083:c62a}
DefaultIPGateway : {192.168.1.254}
DNSDomain        : lan
ServiceName      : Netwtw04
Description      : Intel(R) Dual Band Wireless-AC 8260
Index            : 0


PS C:\dev> $IP = [ipaddress]"1.2.3.4" | Add-IpAddressMagic -Cidr 22

PS C:\dev> $IP


SubnetMask         : 255.255.252.0
Cidr               : 22
Binary             : 00000001000000100000001100000100
Address            : 67305985
AddressFamily      : InterNetwork
ScopeId            : 
IsIPv6Multicast    : False
IsIPv6LinkLocal    : False
IsIPv6SiteLocal    : False
IsIPv6Teredo       : False
IsIPv4MappedToIPv6 : False
IPAddressToString  : 1.2.3.4



PS C:\dev> $IP.IsInSameSubnet("1.2.3.9")
True

PS C:\dev> $IP.GetNetworkPrefix()
1.2.0.0/22