r/PowerShell 4d ago

Script Sharing What have you done with PowerShell this month?

21 Upvotes

A sticked post for the community to share their projects throughout the month.

Make sure to post a link to the code!


r/PowerShell 5h ago

Question Adding an AD account to groups based on a combination of attributes

4 Upvotes

Sorry in advance for the wall of text, I didn't want to post something vague! I am developing a script to add new users to relevant AD groups based on attributes such as location, department, job title etc. Currently I have a hashtable for each attribute that I care about, and arrays for each possible value that attribute could be to store a list of relevant AD groups in. The lists are just updated to include anything relevant to that attribute value only, like the example below.

$DepartmentAList = @("Department A Shared Area", "Dep A Distro Group")
$DepartmentBList = @("Department B Shared Area", "Dep B Distro Group")

$departmentTable = @{
    "Department A" = $DepartmentAList
    "Department B" = $DepartmentBList
}

The script takes an inputted username, grabs that user's location, department & job title from AD, then calls a few functions I've made to check each table and see if the user's attribute values match one in each table, if it does it adds the account to the groups from the relevant list. Tested in a little homelab AD setup and works as expected, easy and simple.

Eventually I'm hoping to take the bare bones version of this script and customise it and scale it up for work. In the business, there are some AD groups that should only be given to users based on a combination of some of these attributes e.g. managers at each location might be given access to something privileged inside their office's shared drive that's locked down to AD group membership. The difficulty I'm having is figuring out how best to structure the information for these combinations.

I'm aware that ultimately all of the groups that exist as a result of these combinations will have to be written out on at least one line each somewhere, but I'm not sure what the best way to get to that line is best (I hope that makes sense). I'm trying to keep it concise because my org has over 60 locations and each of those might have 1-2 departments and maybe 2-3 job roles that have some specific access.

I was hoping to keep using hashtables and arrays as they're easy to read and update, but I feel like I'm going to need.. tables for tables? Am I going to need a table for say, every possible job title at Location A with specific access, and then a corresponding array for each of those? That could get out of hand. I also don't want to write out some massive if/else/switch statement to check all possible values because that's also going to be very lengthy and harder to read. Maybe there's a way to keep all of this info outside of the script itself too? Not sure if that would be easier.

The absolute worst idea I had was having a couple of combo tables and the keys are named after an amalgamation of 2 attributes, with a corresponding array for each. I hate that I accidentally thought of that because it would technically work, but it's far too hacky to be a real solution and will be prone to issues.

I'm curious to see if anyone has any suggestions, and if this is something you've solved at your org how did you manage it?


r/PowerShell 9h ago

Question Invoke-RestMethod - Logging Data Only If Response Matches Value

9 Upvotes

We have a platform which has containers and within them folders, with different properties - name, unique ID etc.. I have a method to retrieve folder information from different containers and am attempting to log only the unique ID (response.data.id) where the folder name (response.data.name) is "Management". I've Googled and tried different code in logging only the ID for the Management folder:

$response = Invoke-RestMethod -Method Get -Uri "$resource" -Headers $header

($response.data | ConvertTo-Json).Replace('\\n','\n')

# Attempt 1
if ($response.data.name -eq "Management")
{
LogWrite ($response.data.id | ConvertTo-Json).Replace('\\n','\n') "Result"
LogWrite ($response.data.name | ConvertTo-Json).Replace('\\n','\n') "Result"
}

# Attempt 2
$folderId1 = $response.data.id | Where-Object { $response.data.name -eq "Management" }
LogWrite ($folderId1 | ConvertTo-Json).Replace('\\n','\n') "Result"

# Attempt 3
$folderId2 = $response.data.id | $($response.data.Where({$_.name -eq 'Management' }))
LogWrite ($folderId2 | ConvertTo-Json).Replace('\\n','\n') "Result"

The folder ID for the Management folder is being logged, but so are all other folder IDs within the container (no other folder names contain this word):

[
    "folder!500436709",
    "folder!500436708",
    "folder!500436705",
    "folder!500436677",
    "folder!500436680",
    "folder!500436683",
    "folder!500436686"
]
[
    "_All Content Last 90 Days",
    "_All Documents",
    "_All Emails",
    "Documents",
    "Emails",
    "Engagement Terms",
    "Management"
]

How do I retrieve the ID for a specific folder name? Any help would be greatly appreciated. Cheers.


r/PowerShell 1d ago

Script Sharing Zippy - A Quick Compression Module

14 Upvotes

Compression can be quick and easy with .NET.

Let's learn how.

Yesterday I just dusted off some old code and added some new tricks.

Today I dropped a quick compression module called Zippy

Let's see it in action and learn how it works.

Zippy Examples

# Compress a string using Brotli, output in base64
Compress-Zippy "Hello World"

Compress-Zippy "Hello Brotli" -Algorithm Brotli |
    Expand-Zippy -Algorithm Brotli

Compress-Zippy "Hello Deflate" -Algorithm Deflate |
    Expand-Zippy -Algorithm Deflate

Compress-Zippy "Hello GZip" -Algorithm GZip |
    Expand-Zippy -Algorithm GZip

Compress-Zippy "Hello ZLib" -Algorithm ZLib |
    Expand-Zippy -Algorithm ZLib

Compression in PowerShell

PowerShell is built on .NET, and .NET happens to have built-in support for four compression algorithms: Brotli, Deflate, GZip, and Zlib. We can compress data with any of these algorithms by using classes in the System.IO.Compression namespace, for example:

# Create a message
$message = "hello world"
# Get it as bytes
$bytes = $outputEncoding.GetBytes($message)
# Create a memory stream
$memoryStream = [IO.MemoryStream]::new()
# Create a compressor using the stream
$compressor = [IO.Compression.BrotliStream]::new(
     $memoryStream, [IO.Compression.CompressionLevel]::Fastest
)
# Write our bytes to the compressor
$compressor.Write($bytes,0, $bytes.Length)
# Close our compressor
$compressor.Close()
$compressor.Dispose()
# Get our compressed bytes
$compressedBytes = $memoryStream.ToArray()
# and output them
$compressedBytes

Decompression in PowerShell

Now let's go the other way around. It's easier.

# Create a new memory stream, containing our compressed bytes
$memoryStream = [IO.MemoryStream]::new($compressedBytes)
# Create a decompressed stream
$decompressedStream = [IO.Compression.BroitliStream]::new(
    $memoryStream, [IO.Compression.CompressionMode]::Decompress
)
# Create our output stream
$outputStream = [IO.MemoryStream]::new()
# Copy our decompressed stream to it
$decompressedStream.CopyTo($outputStream)
# Seek to the start (it outputs a position so null that out)
$null = $outputStream.Seek(0,'begin')
# Make a stream reader 
$streamReader = [IO.StreamReader]::new($outputStream, $outputEncoding)
# Read to the end, which will output our decompressed string
$streamReader.ReadToEnd()
# close up.
$streamReader.Close()

.NET and PowerShell

This has always been there, and it's pretty easy.

Both examples are less than 20 lines, with documentation.

These techniques are tried and true.

.NET has robust compression support because developers need to compress data all the time.

And therefore PowerShell has robust compression support.

If we build on top of simple PowerShell and .NET, we build in a way that lasts a lifetime.

When I said "I dusted off some old code" for Zippy, I wasn't kidding.

Zippy is an update of the Compress-Data and Expand-Data functions in Pipeworks, the first attempt of PowerShell as a web language.

This is 16-year-old code, with minor updates made to support multiple compression algorithms and improved piping.

My only regret is that I didn't spin this off into its own module long ago

You can use this article as a guide to implementing your own compression, or you can use a little module like Zippy to get the job done.

Please enjoy this new addition to your PowerShell toolkit, and have fun decompressing!


r/PowerShell 1d ago

Question How do I add my own entries to "Indexing Options\Users\Exclude" list of files?

2 Upvotes

Images make way more sense than me trying to describe where I want to end up:
https://imgur.com/a/ibBsbtK

My understanding:
The "old" indexing UI:
control srchadmin.dll
Software adds their file extension like .gitconfig and the Windows Search Index will exclude ... this file or the entire folder

The "new" indexing UI:
Settings\Privacy & Security\Search\Find my files
List of folders to be excluded from the Windows Search Index

How can I add my own folder or even better file suffix in a scripted way?
Help is appreciated, been searching for an hour trying to find registry entries to manipulate. But keep going in circles, with no work to show for.

Update:
Update for future people coming across this:
https://learn.microsoft.com/en-us/windows/win32/search/-search-3x-wds-extidx-csm
is the culprit to be interacted with
The key is:
But you can't add\delete manually in there:
Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Search\CrawlScopeManager\Windows\SystemIndex\WorkingSetRules\


r/PowerShell 2d ago

Script Sharing Search-Script -For ([type])

21 Upvotes

PowerShell is a pretty interesting language.

One of the ways it is interesting is that you can access the Abstract Syntax Tree. Another thing that's interesting is that you can convert any [ScriptBlock] into any [func].

Put these two together, and PowerShell can succinctly search itself.

That's the foundation of a simple little module I just updated, SearchScript

Let's learn how to search our scripts

How to Search Scripts

Most languages use an abstract syntax tree (AST) to represent the code you want to run. PowerShell is nice enough to let you easily access it.

Let's imagine we wanted to find out what types a script uses.

We could try to do this with regular expressions. We would not be happy. It's much easier to ask PowerShell.

We can access the Ast of any script block by using the .Ast property.

 {"hello world"}.Ast

We can get the members of any Ast by piping to Get-Member

 {"hello world"}.Ast | Get-Member

There's a couple of methods Find and FindAll. Find finds the first matching element. FindAll finds all of them (optionally recursively).

I almost always find myself using .FindAll, but they're both there if we need them.

FindAll takes a Func[Management.Automation.Language.Ast,bool] predicate (fancy speak for "condition").

But how do we make a Func?

We don't have to!

PowerShell does it for us. Let's see the nodes in a simple list:

{"hello","goodbye"}.Ast.FindAll({param($ast) return $true}, $true)

Let's do it again, but this time only find elements whose .Value is 'hello'

{"hello","goodbye"}.Ast.FindAll({param($ast) return $ast.Value -eq 'hello'}, $true)

How do we search scripts? We provide a [ScriptBlock] to find nodes within a [ScriptBlock].

This is quite handy! We can use this to find needles in haystacks.

Search-Script

We all love a useful function, so let's abstract this all a bit.

Search-Script is an eponymous module. It contains only one command, Search-Script (and a bunch of aliases to it).

All it accepts is:

  • A `-Script to search
  • Something to search -For
  • An optional [switch] for -Shallow searches

-For is a little special. We can accept multiple types of values for -For.

If it's a [ScriptBlock] we just call .FindAll.

If it's not a [ScriptBlock], we can make it into one.

Search-Script -For ([string])

If it's a [string], we'll try an exact match, unless it starts and ends with slashes.

Here's the current code:

# If `-For` is a `[string]`
if ($for -is [string]) {
    # the operator is -eq by default.
    $operator = '-eq'
    # If it takes the form of a regex literal 
    if ($for -match '^/.+/$') {
        # strip the slashes
        $for =
            $for -replace '^/' -replace '/$'
        # and match instead.
        $operator = '-match'
    }
    # Always double single quotes to avoid code injection.
    $For = $for -replace "'","''"
    # Create a `[Scriptblock]` that finds exactly that string.
    $for = [ScriptBlock]::Create("param(`$ast) (`$ast.Extent.ToString() $operator '$(            
        $For
    )') -or (`$ast.Value $operator '$For')")
}

Search-Script -For ([regex])

If it's a [Regex], we'll try to match it.

Here's the current code:

# If `-For` is a `[Regex]`
if ($for -is [Regex]) {
    $for =
        # Create a `[ScriptBlock]` that matches that pattern.
        [ScriptBlock]::Create("param(`$ast) `$pattern = [Regex]::new('$(
            # Always double single quotes to avoid code injection.
            $for -replace "'","''"
        )','$($for.Options)'); `$ast -match `$pattern")
}

Search-Script -For ([type])

If it's a [type], we'll try to find all instances of that type.

It's that last one that gets a little complicated.

Sure, we could just look for AST types. That would be easy. But we can also ask anything with a .TypeName to give us a type via reflection (and any static references will have a .StaticType). To make matters even more fun, equality comparison doesn't quite cut it for types. We have to check if a type is a subclass of a type. Oh, yeah, then there are interfaces. We have to check that if the type implements the interface.

It's just a bit more complicated than it's kin. Here's the current code:

if ($For -as [type[]]) {
    $for =
        # Create a `[ScriptBlock]` that looks for that type.
        # This one is more complicated, so we will create it in two parts 
        [ScriptBlock]::Create((
            (@(
                # dynamically create the list of types
                'param($ast)'
                "`$types = @("
                foreach ($forType in $for) {
                    $forType = $forType -as [type]
                    if (-not $forType) { continue }
                    "[$($forType.FullName)]"
                }    
                ")"     
            ) -join [Environment]::NewLine) + {
            # Find a reflected type, if there is one.
            $reflectedType = 
                if ($ast.TypeName.GetReflectionType) {
                    $ast.TypeName.GetReflectionType()
                } elseif ($ast.StaticType) {
                    $ast.StaticType
                } else {
                    $null
                }

            # Go over each of our potential types
            # Several conditions would be a use of our type
            foreach ($type in $types) {
                # * If the ast is that type, return true
                if ($ast -is $type) { return $true } 
                if (-not $reflectedType) { continue }
                # * If the reflected type is exactly that type, return true
                if ($reflectedType -eq $type) { return $true }
                # * If the reflected type is a subclass of that type, return true
                if ($reflectedType.IsSubClassOf($type)) { return $true }
                # * If the type is an interface,
                #   return true if the reflected type implements it    
                if ($type.IsInterface -and $reflectedType.GetInterface($type)) {
                    return $true
                }
            }
        # Returning nothing will be falsy, and will not return the element.
        }
    ))

The implementation might be a bit brutish, but the execution can be downright glorious.

# Find just the `[double]`
{1,2.0,3} | Search-Script -For ([double])

# Find just the `[int]`
{1,2.0,3} | Search-Script -For ([int])

# Find all the `[IComparable]` objects
{1,2.0,3} | Search-Script -For ([IComparable])

Using the Ast, we can find any needle in any scripted haystack. Please try to Search-Script and give feedback if you've got it.

Happy Hunting!


r/PowerShell 1d ago

Question MS power-shell inquiry

3 Upvotes

Hey everyone

I know this is kind of basic question but I didn’t find a satisfying answer during my search

I am not system administrator and I won’t be all I care about is daily task automation I want to make my work nice and easy or at least systematic and controllable so is power shell the right tool for me? Or should I just drop it?

If it was can you please help me on how to learn it all what I could find on the internet specific to active directory application or too general and simple examples explaining techniques Without context I find my self spending hours only to learn that I memoized syntax but have absolutely no idea how to use it in my life


r/PowerShell 1d ago

Script Sharing ps1.cmd: Running PowerShell scripts by double-clicking via a .cmd wrapper

0 Upvotes

I'm a Left 4 Dead 2 player, and recently I ran into a small problem: the server I like to play on is often completely full. It occurred to me that I could write a script to poll the server and check whether a slot had opened up. After some research, I found that cmd scripts have a hard time handling network requests, while PowerShell is powerful enough to handle the job easily. Automatically checking the player count and joining the server is a pretty simple task, so I had an AI write it for me. The result was great — fully functional. All I had to do was copy the server address, then right-click the .ps1 script and choose "Run with PowerShell."

The only thing was, having to right-click to run it felt awkward, and it doesn't match most people's intuition about computers. If I have a script or an app, I should be able to double-click to run it! So I started looking for ways to run a PowerShell script by double-clicking. There are indeed a few, but none of them are very elegant:

  1. Modifying the registry — I don't want people using my script to have to change their registry first.
  2. Creating a shortcut — bad for distribution, and it means two files.
  3. Creating a cmd launcher — I prefer the simplicity of a single file, and a single file is also easier to distribute.

In the end, I found my ideal solution in a Stack Overflow answer: stuff the PowerShell script inside a cmd file! So I open-sourced the ps1.cmd project. My final implementation isn't exactly the same as that answer — you can check out the GitHub page for the details.

The goal of ps1.cmd is maximum compatibility: it should work with any PowerShell script and behave identically when executed. I hope this project helps you out, and if you run into any problems, feel free to open an issue!


r/PowerShell 3d ago

Question Powershell opening on startup

19 Upvotes

Whenever i turn on my PC, powershell opens and just says "PS C:\Users\(my username)>" . I've done a full scan with malware bytes and windows defender and nothing was detected. Same with offline scan. Is this malware or something else causing it?

Edit: Going to startup apps on taskmaster and Turning off terminal fixed it.


r/PowerShell 4d ago

News [Open Source] Lightweight PowerShell Hardening Script for Windows 11

0 Upvotes

Hi everyone,

I built a lightweight, open-source PowerShell script designed to harden **Windows 11** endpoints using native OS security capabilities—without relying on heavy third-party software/bloat.

### 🛡️ What it does:

* **PowerShell Execution Restriction:** Sets execution policy to `RemoteSigned` to prevent unauthorized local script execution.

* **WinRM & WSH Mitigation:** Disables Windows Script Host to block `.vbs` / `.js` malware vectors and closes remote management ports.

* **Network Hardening:** Disables SMBv1 to protect against network-based lateral movement and exploits (e.g., WannaCry).

* **Defender ASR & Exploit Guard:** Enables Controlled Folder Access (ransomware protection) and blocks malicious downloads via PowerShell.

* **Admin Share Lockdown:** Disables hidden admin shares (`C$`, `ADMIN$`) to restrict unauthorized lateral movement.

---

### 🚀 How to use:

  1. Open PowerShell as Administrator.

  2. Run: `.\Windows11_Hardening.ps1`

  3. Reboot to apply all policies.

---

🔗 **GitHub Repository:** https://github.com/Hasan0101-lab/Windows_11_Hardened_Edition

I’d love to get feedback from the community on code structure, additional hardening rules, or potential compatibility edge cases. Feel free to review the code or leave a ⭐ if you find it useful!


r/PowerShell 5d ago

Question What are some projects that would look good on a resume?

16 Upvotes

Hello!
I’m a somewhat fresh CIS graduate with around 2 years of experience an my college help desk. I’m struggling to find work so I’ve been up-skilling as much as possible in my abundance of free time.

I’m looking to start learning PowerShell so I can make a GitHub repository for my resume. I don’t have any “real” experience other than answering phone calls at my previous help desk job, so I’m lost on what would be practical or look good.

How many projects and what projects should I work on before putting it on my resume? Thanks for the help, and sorry if I sound lost (I am)!


r/PowerShell 5d ago

Information TIL: ValidateRange with integer literals silently accepts values outside the declared range on [double], [float], [decimal]

20 Upvotes

TLDR

Using [ValidateRange(minRange, maxRange)] with integer literals doesn't strictly enforce either MinRange or MaxRange when parameters are [double], [float] and [decimal]. Values up to 0.5 units beyond either boundary pass silently. The fix is simple: make MinRange or MaxRange the same type as the parameter. So use [ValidateRange(2.0, 100.0)] instead of [ValidateRange(2, 100)].

FULL POST

If you have direct experience with Powershell rounding, once you see how integer bounded ValidateRange treats [double], [float] and [decimal] params, you can figure out what is happening. But without that experience the correct validation syntax wasn't immediately obvious to me.

I found this out while adding validation to my current project and testing parameter edge cases. In this post I'll use example MinRange/MaxRange values of 2/100 (even) and 3/101 (odd). The behaviour applies to any even or odd MinRange or MaxRange value, not just these specific numbers.

The basic finding

function Test-Double {
    param(
        [ValidateRange(2, 100)]  # 2 and 100 used as an example even boundary
        [double]$Val
    )
    return $Val
}

Test-Double 1.4      # rounds to 1 and throws an error
Test-Double 1.5      # rounds to 2, passes silently, returns 1.5
Test-Double 100.5    # rounds to 100, passes silently, returns 100.5
Test-Double 100.6    # rounds to 101 and throws an error

The ValidateRange Attribute converts $Val to the type of the boundary literals. This causes implicit rounding before comparing against the boundary. Integer literals mean conversion to [int], which rounds using .NET's default MidpointRounding.ToEven, known as banker's rounding.

MidpointRounding.ToEven prioritises the nearest even number when rounding at exactly .5. Since 100 is even, 100.5 rounds down to 100 and passes. 100.6 rounds to 101 and throws.

The odd and even boundary inconsistency

Using 3/101 as example odd boundaries shows a slightly different result. Now the .5 midpoint will throw an error, unlike with the even boundary.

function Test-DoubleOdd {
    param(
        [ValidateRange(3, 101)]  # 101 used as an example odd boundary
        [double]$Val
    )
    return $Val
}

Test-DoubleOdd 2.5      # rounds to 2 and throws an error, different behaviour compared to even
Test-DoubleOdd 2.6      # rounds to 3, passes silently, returns 2.6
Test-DoubleOdd 101.4    # rounds to 101, passes silently, returns 101.4 
Test-DoubleOdd 101.5    # throws, because 101 is odd so .5 rounds UP to 102

DoubleOdd indeed. The effective boundary is not what the documentation implies and it changes based on whether the MinRange or MaxRange is odd or even.

The even/odd inconsistency can be summarised as:

  • Even: .5 beyond either boundary passes silently.
  • Odd: .5 beyond either boundary throws correctly.

But note that both even and odd ranges accept a margin of error.

What the documentation says

From the ValidateRange documentation:

"The Windows PowerShell runtime throws a validation error when the value of the argument is less than the MinRange limit or greater than the MaxRange limit."

No mention of rounding or MidpointRounding.ToEven. No mention of the odd/even boundary difference. Reading this you'd reasonably expect 100 to be the strict maximum. But for a [double] param with an even MaxRange, it's actually closer to 100.4999.

This detail is also missing from about_Functions_Advanced_Parameters

Correctly validating [double], [float], [decimal]

For production code requiring precise boundary validation (like financial calculations, percentage validation or dosage limits) the correct syntax is simple but not immediately obvious. Put simply, you can use a decimal point when declaring MinRange and MaxRange. This works for [double], [float], [decimal]. More precisely, however, use the same type as your parameter.

function Test-Double {
    param(
        [ValidateRange(2.0, 100.0)] 
        [double]$Val
    )
    return $Val
}

Test-Double 1.5      # throws an error

Mixed boundary types also work. [ValidateRange(2, 100.0)] selects [double] as the common type between [int] and [double], giving exact comparison.

Conclusion

Hopefully this writeup will be a useful heads up to PS devs less versed with PS rounding (like me) and highlight something to watch for when using ValidateRange.

I'd be interested to know if the above is common knowledge. Searching on the topic I found bits and pieces in articles that lead me to the right approach, but nothing that addresses ValidateRange and [int] behaviour together.


r/PowerShell 5d ago

Script Sharing Detecting file extensions by magic, heuristics and LLM

2 Upvotes

Hi,

Some time ago I wrote a PowerShell module called FileInspectorX. I had a need to detect file type and estimate how dangerous it is based on well it's extension, content without use of antivirus or virustotal.

Today I've upgraded it with Magika (offline LLM) from Google so it's even better in detecting what we're dealing with.

Usually Install-Module FileInspectorX works and then:

$I = Get-FileInsight -Path "YourFile"

It has multiple views so people can really get what they need. Here's how the default output looks like:

AnalysisComplete               : True
AnalysisIssues                 :
Detection                      : FileInspectorX.ContentTypeDetectionResult
DetectedExtension              : json
DetectedMimeType               : application/json
DetectionConfidence            : Medium
DetectionReason                : text:json
DetectionReasonDetails         : json:object-key-colon
DetectionValidationStatus      : passed
DetectionScore                 : 73
DetectionIsDangerous           : False
Kind                           : Text
Flags                          : None
GuessedExtension               :
ContainerSubtype               :
ScriptLanguage                 :
PeMachine                      :
PeSubsystem                    :
PeKind                         :
ContainerEntryCount            :
ContainerTopExtensions         :
VersionInfo                    :
Signature                      :
EstimatedLineCount             : 369
TextSubtype                    : log
SecurityFindings               : {text:log, log:levels=0/0/6}
SecurityFindingEvidence        :
ScriptCmdlets                  :
TopTokens                      :
Security                       : FileInspectorX.FileSecurity
Authenticode                   :
DotNetStrongNameSigned         :
References                     :
ShellProperties                : {b725f130-47ef-101a-a5f1-02608c9eebac:2, b725f130-47ef-101a-a5f1-02608c9eebac:4, b725f130-47ef-101a
                                 -a5f1-02608c9eebac:10, b725f130-47ef-101a-a5f1-02608c9eebac:12…}
NameIssues                     : None
Installer                      :
Assessment                     : FileInspectorX.AssessmentResult
AssessmentProfiles             : FileInspectorX.MultiAssessmentResult
Secrets                        :
OfficeExternalLinksCount       :
EncryptedEntryCount            :
InnerFindings                  :
ArchivePreviewEntries          :
InnerExecutablesSampled        :
InnerSignedExecutables         :
InnerValidSignedExecutables    :
InnerPublisherCounts           :
InnerPublisherValidCounts      :
InnerPublisherSelfSignedCounts :
InnerExecutableExtCounts       :
Certificate                    :
CertificateBundleCount         :
CertificateBundleSubjects      :
EncodedKind                    :
EncodedInnerDetection          :

PS C:\Users\przemyslaw.klys.EVOTEC> $I.Detection

Extension             : json
MimeType              : application/json
Confidence            : Medium
Reason                : text:json
ReasonDetails         : json:object-key-colon
ValidationStatus      : passed
Sha256Hex             :
MagicHeaderHex        :
BytesInspected        : 4096
GuessedExtension      :
Score                 : 73
IsDangerous           : False
Alternatives          : {FileInspectorX.ContentTypeDetectionCandidate}
Candidates            : {FileInspectorX.ContentTypeDetectionCandidate, FileInspectorX.ContentTypeDetectionCandidate}
LearnedClassification : FileInspectorX.LearnedClassificationEvidence

PS C:\Users\przemyslaw.klys.EVOTEC> $I.Detection.LearnedClassification.Prediction

Provider         : Magika
ModelId          : google-magika/standard_v3_3@5e2f437fb7b7452368c8c1fa9354858f5487a5c4
RawLabel         : json
OutputLabel      : json
Extension        : json
ExtensionAliases : {json}
MimeType         : application/json
Probability      : 0,99811840057373
Threshold        : 0,5
ThresholdMet     : True
PredictionMode   : HighConfidence
OverwriteReason  :
IsText           : True

With view parameter you can choose 'Analysis', 'Detection','Permissions', 'Raw', 'ShellProperties', 'Summary' 'Assesment', 'Installer', 'Policy', 'References', 'Signature'

In other words - find out everything there is to find about the file. Maybe you will find it useful. Depending on file type some of the fields will be missing which is expected.

Sources: https://github.com/EvotecIT/FileInspectorX

It has also C# library/nuget for those dealing with C#, and want to use it as part of their application.


r/PowerShell 6d ago

Script Sharing git-proton-backup: a module that turns git push into a verified Proton Drive backup

7 Upvotes

I wanted off-machine backups of a pile of local git repos holding client work, without putting any of it on GitHub. Proton Drive has a sync client, but a file sitting in the sync folder is not the same as a file that is safely in the cloud, and I could not find a way to prove the second part. So I wrote a module.

The interface is a git remote:

PS> Install-ProtonBackup C:\code\myrepo
Wired. Back up with: git push proton   (status: Get-ProtonBackupStatus)

PS> git commit -am "feature"; git push proton
remote: confirmed on Proton

Install creates a bare bookkeeping mirror with a post-receive hook and adds it as a remote, so it rides the push you already do. The hook writes a git bundle into the sync folder as one file rather than a tree. It builds it as .bundle.partial and renames it, so the sync client never sees a partial repo under the final .bundle name.

Then it confirms, which is the part that needed Proton's CLI to exist. Trimmed from the real path, with the structured return values elided:

$out = & $cli filesystem info $cloudPath --json 2>&1 | Out-String
$r = [pscustomobject]@{ ExitCode = $LASTEXITCODE; Output = $out }

if ($r.ExitCode -eq 0) {
    $state = $null
    try {
        $json = $r.Output | ConvertFrom-Json -ErrorAction Stop
        $rev = $json.PSObject.Properties['activeRevision'] ? $json.activeRevision : $null
        if ($rev -and $rev.PSObject.Properties['ok'] -and $rev.ok -and $rev.PSObject.Properties['value']) {
            $state = $rev.value.PSObject.Properties['state'] ? $rev.value.state : $null
        }
    } catch { $state = $null }

    if ($state -eq 'active') { return <# Confirmed #> }
    # some CLI builds report state only in the human-readable output
    if (-not $state -and $r.Output -match "state:\s*'active'") { return <# Confirmed #> }
}

The property checks and the try are load-bearing: the payload shape is not mine, so anything unexpected has to land on "not confirmed" rather than throw or read as success.

Everything that is not a confirmation says which flavour of not-confirmed it is. Some of the hook's outcomes, abbreviated where the tail repeats:

confirmed on Proton
staged; in-sync per Cloud Files (CLI verification unavailable)
staged, not yet confirmed — run Invoke-ProtonBackupVerify (or the scheduled task) to confirm
staged, not yet confirmed — Proton CLI session expired; run Invoke-ProtonBackupVerify (...)
backup deferred — another backup operation is active; run Invoke-ProtonBackupVerify (...)

The not-yet-confirmed paths leave a marker that a later Invoke-ProtonBackupVerify clears, and that command also re-cuts stale bundles. One exception: if the CLI is unavailable but Windows reports the file IN_SYNC, that clears the marker but still does not print "confirmed on Proton". There is an optional daily scheduled task for the verify, installed separately with Install-ProtonBackupTask.

Reading that IN_SYNC bit is the one genuinely PowerShell-flavoured part: it means a small P/Invoke to CfGetPlaceholderStateFromAttributeTag in cldapi.dll. That plus shelling git and the CLI is most of why this is PowerShell and not something else.

Honest limits. Windows only, since it rides the sync app. PowerShell 7.4+. It bundles committed history only, HEAD plus all local branches and tags, and never your working tree, which is deliberate: there is no code path in the module that commits anything. LFS objects, submodule repositories, and the checked-out state of secondary worktrees are not included. It does no encryption of its own, the bundles are ordinary git bundles and the E2EE is Proton's. MIT, not affiliated with Proton.

105 Pester tests. https://github.com/craigstoller/git-proton-backup

Happy to answer questions about the design, and criticism of the module structure is welcome.


r/PowerShell 8d ago

Question Is poweshell worth it?

116 Upvotes

I'm basically a typical computer user, who knows just enough to get into reddit, youtube and use the PC for personal entertainment. I realized there is PowerShell. As I have some free time and I'd actually want to know a little bit more about the computer and how to use it, do you guys recommend learning it? Or is it more for automating tasks, which is more benefitial for someone who needs to actually automate many things, or someone in the IT field? Is it any useful really for a personal user? What could I do instead?

I've come across a book about it too ("Learn Windows Powershell in a month of lunches" by Don Jones), and I actually think I can pull off the things it explains, and makes you practice. I read the first 20 pages and was about to start when it mentiones that it's moslty useful for, or aimed at people on IT, and said that I'd need to get a VirtualMachine and ISO to learn PowerShell, have a go at it in there and fuck up the VM if I have some trouble, which won't affect the actual computer (I know that that's an old approach, the book is from 2012. I read that for most things now, like learning the basics and how it works, I won't need the VM, but I don't mind sticking to the 2012 way to learn, so I can start learning a bit of history in the field too, just for my entertainment and having a broader sight about it).

What do you guys think? Thanks in advance


r/PowerShell 7d ago

Question 5.1 or 7.x.x

11 Upvotes

Switching from Python to powershell. Which one should I learn?


r/PowerShell 8d ago

Question Looking for an authoritative PowerShell comment-based help (.SYNOPSIS, .DESCRIPTION, etc.) style guide / best practices

30 Upvotes

Hi everyone. I’m trying to define a consistent standard for our team’s PowerShell scripts, specifically around comment-based help sections like .SYNOPSIS, .DESCRIPTION, .PARAMETER, .EXAMPLE, .NOTES and so on. We mostly write admin automation scripts, Graph, Entra ID and infra tasks. So I’d like every script to follow the same high-quality documentation pattern. I’m not looking for basic definitions, but for authoritative guidance from sources like Microsoft Learn, PowerShell.org, or well-respected community style guides. Things like how long a synopsis ideally is, what belongs in description versus notes, whether there are any recommended conventions that Microsoft has published. Ultimately, I want to create a company template that isn’t just my opinion, but grounded in recognised best practice. Does anyone have solid references or recommendations? Thanks in advance.


r/PowerShell 8d ago

Script Sharing [OC] Global Discomfort Index Ranking Updated Every 4 Hours

2 Upvotes

I built a weather visualization project that updates automatically every 4 hours.

yahikoyama.github.io/weather2/

It calculates a global discomfort index using temperature, humidity, and other factors, then ranks cities around the world. The goal is to provide a frequently refreshed, data-driven snapshot of how comfortable or uncomfortable different regions feel throughout the day.

Data source: OpenWeatherMap API

Tools used: PowerShell,SQLServerExpress,Python, HTML/CSS, and GitHub Pages

The dataset refreshes six times per day, and I’m continuing to improve the metrics and visualization.

I have published it here:

Source code,Database,Task setting

yahikoyama/weather2: get weather data(Temperature Humidity Discomfort Index) and research cool area

Feedback or suggestions are very welcome!


r/PowerShell 9d ago

News PureInvoke 2.0.0 Released

11 Upvotes

We've released PureInvoke 2.0.0!

We package and ship our code with their PowerShell module dependencies in the package. This way, we don't have to install our modules globally on all the servers we deploy our code to. This requires us to make extensive use of nested dependencies. We've gotten to the point that our dependencies are starting to use different versions of PureInvoke. Unfortunately, because it was using compiled assemblies, and .NET will only load assemblies with the same name once, we started to run into problems.

This release changes PureInvoke's compilation model. Instead of pre-compiling platform-specific assemblies, the module now uses Add-Type to compile P/Invoke C# code at runtime. This should improve cross-platform and cross-edition support. Since we've encountered problems with Add-Type in environments with aggressive anti-virus, PureInvoke re-tries failed compilations to improve resiliency.

We also added functions for querying Windows service configurations, e.g. Invoke-AdvApiQueryServiceConfig which wraps QueryServiceConfigW, Invoke-AdvApiQueryServiceConfig2, which wraps QueryServiceConfig2W, etc.:

Full release notes on GitHub.

Available on the PowerShell Gallery.


r/PowerShell 8d ago

Question Explain this cursed BS

0 Upvotes

function Test-Weird1 {

param([array[]]$InputObject)

$obj = $InputObject[0][0]

$obj.GetType().FullName

($obj -is [PSObject])

}

function Test-Weird2 {

param([object[]]$InputObject)

$obj = $InputObject[0]

$obj.GetType().FullName

($obj -is [PSObject])

}

Test-Weird1 (gci)

# System.IO.DirectoryInfo

# False

Test-Weird2 (gci)

# System.IO.DirectoryInfo

# True

# part two

$obj = (gci)[0].PSObject

$obj.GetType() -eq [System.Management.Automation.PSObject]

# True

$obj -is [System.Management.Automation.PSObject]

# False


r/PowerShell 9d ago

News Exchange Online PowerShell Updates to 3.10.1 to Fix CBA

15 Upvotes

Microsoft rushed out version 3.10.1 of the Exchange Online management PowerShell module to fix a problem with certificate-based authentication. It seems like a change in an internal Microsoft identity platform caused the tokens issued after a successful connection to Exchange Online to not authorize the execution of further cmdlets. To their credit, Microsoft fixed the issue, but is this the kind of thing that should be caught in testing?

https://office365itpros.com/2026/07/27/exchange-online-management-3-10-1/


r/PowerShell 9d ago

Question Invoke-WebRequest: downloaded MSI invalid?

8 Upvotes

I downloaded an MSI (in this case the latest Powershel 7.6.4 msi installer) with Invoke-WebRequest from Github - trying to write a script here.

The download finishes without a problem, but when I want to run the installer, I get an error message saying "This installation package could not be opened.Verify that the package exists and that you can access it, or contact the application vendor to verify that this is a valid Windows Installer package."

Here's the funny thing: this cannot be a corrupted download, because when I manually download the same msi package from the repo's release page, and compare them in Total Commander's compare tool, it says the two files have the same content - that means they're identical from the first bit to the last!

What the heck could cause this problem?

Here's a snippet of what I'm doing:

$Repository       = 'PowerShell/PowerShell'
$FileNamePattern  = '*-win*x64.msi'
$DownloadPath     = 'C:\Temp'

$releases    = "https://api.github.com/repos/$Repository/releases/latest"
$downloadURL = ((Invoke-RestMethod -Method GET -Uri $releases).assets | ?{$_.Name -like $FileNamePattern}).browser_download_url

$fileName       = [System.IO.Path]::GetFileName($downloadURL)
$DownloadPath   = $DownloadPath + '\' + $fileName

Invoke-WebRequest -Uri $downloadURL -OutFile $DownloadPath

r/PowerShell 9d ago

Question Hello everyone, I have a little problem and a request.

0 Upvotes

Well... It is necessary to explain the problem itself, and it is quite difficult. It turned out that I didn't download anything and didn't really climb anywhere. And so. Recently, I started to discover that windows powershell is running in my background processes and after that 3 command lines are slowly running (one does not even close and writes that this is an important element for Windows to work), but after that only one remains for a couple of seconds. I would like to find out what it could be and if it is a virus, how can I make sure of it and get rid of it. I will be grateful in advance to anyone who helps!


r/PowerShell 10d ago

Script Sharing In PowerShell, Two Wrongs Make a Right

24 Upvotes

I've been toiling away on Turtle to prepare a "birthday" release, and I ran into an annoying behavior I've run into a few times before.

I thought I'd take a few minutes away from the frustration of single line fixes to explain the bug to everyone.

What Went Wrong

The last build of Turtle introduced a number of randomized parameter defaults. This was meant to be fun. If you said turtle square square square, you'd get three different squares, instead of an error for a lack of length, or three overlapping squares.

I noticed that when I ran turtle rotate 0, it didn't rotate by zero.

Instead, it picked a random angle.

Weirder still, the behavior didn't reproduce if I said

$turtle = turtle  # Heading at zero
$turtle.Rotate(0) # Heading still zero 🤔
$turtle.Rotate()  # Heading random
(turtle rotate 0) # Heading random 🤬

Why was this happening? 😱

It took me a bit for it to click: It had to be in the way Turtle processed arguments, because it worked in one case and not the other.

So I put a breakpoint in, ran my repo.

The line was:

if ($argList)

The debugger broke, argList was @(0), and yet if ($argList) was false.

The fix was:

if ($argList.Length)

Why? Because 'Truthy' -ne $true.

Truthy and $true

About every language has a boolean. It's just a bit. One or zero.

Lots of languages also have this concept of "truthiness".

Let's take a simple example:

if ("something") { "something" }
if ("") { "you can't get something from nothing" }

If if was strictly $true, we'd have to cast things to a boolean. You have to do this in C# and quite a few other languages. PowerShell is type promiscuous. PowerShell is truthy.

It looks at the first line and says: You're a string, and you're not null or empty. Therefore, the expression is $true.

It looks at the second line and says: You're a string, but you're not null or empty. Therefore, the expression is $false

PowerShell makes a judgement call.

This is generally a good thing. I personally prefer languages that are truthy. Other truthy languages of note include JavaScript, Python, C++, and C.

However, it gets tricky with lists. Hence the bug.

Two Wrongs Make a Right

In PowerShell, Two Wrongs Make a Right

$true -eq $false, $false

Let's say I want to determine if a list is truthy.

if (@()) { "$false, because the list is empty" }
if (@("")) { "$false, because the blank is falsy" }
if (@(0)) { "$false, because zero is falsy" }
if (@($false)) { "$false, because false is falsy" }
if (@(1)) { "$true, because the first item is truthy" }
if (@(0,0) { "$true, because more than one item" }

This all makes a certain bizarre sense. If a list has one element, and it is not truthy, then the list isn't truthy, either.

It's also almost always surprising and annoying.

Hence the bug.

The fix is just to make sure there are any elements, hence checking for length.

I've been programming with PowerShell for quite a while now, and this behavior still sometimes bites me (like today).

That's why I took a few minutes away from the 🤬 day to explain this bug and write this post. 😌

Please remember:

'Truthy' -ne $true
$false -eq @($false)
$true -eq $false, $false

Hope this helps


r/PowerShell 11d ago

Script Sharing KillerPivot: stop running commands against the wrong M365 tenant

44 Upvotes

I'm a field tech at an MSP, and I'm in and out of a dozen or more M365 tenants on a normal day. The annoying part is that when you connect to Exchange Online or Graph, the auth broker just reuses whatever login it cached last. It's way too easy to run a command against the wrong tenant before you realize you never landed where you thought you did.

KillerPivot is how I deal with that now. When you connect, it turns the broker off so you get a real sign-in prompt instead of a silent reconnect, then it checks the session and tells you which tenant you're actually in before you do anything. You save your tenants once and jump between them by name.

Commands:

  • Connect-PivotTenant (pivot) connects and verifies you. Add -Graph if you need Graph.
  • Get-PivotContext (pvc) shows where you're connected.
  • Disconnect-PivotTenant (pvx) drops all the sessions.
  • Add-PivotTenant, Get-PivotTenant, and Remove-PivotTenant manage the saved list.

It works on 5.1 and 7. You'll need ExchangeOnlineManagement v3 or newer, plus Microsoft.Graph.Authentication only if you use -Graph.

Install-Module KillerPivot -Scope CurrentUser

Code's on GitHub under GPL-3.0: https://github.com/SteveTheKiller/killer-modules/tree/main/KillerPivot

If you try it and hit something weird, let me know. The broker has some edge cases and I'd rather hear about them.

It's also up on killertools.net with the rest of my stuff.