Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Pile Of Garbage
May 28, 2007



It's worth remembering that objects in PowerShell don't behave like you'd expect with normal OO languages which is due to the PSObject layer: https://blogs.msdn.microsoft.com/besidethepoint/2011/11/22/psobject-and-the-adapted-and-extended-type-systems-ats-and-ets/. This is why you can call the Count property of a System.Int32 struct despite it not having any properties:

code:
PS C:\> [System.Int32]$Foo = 219
PS C:\> $Foo.GetType().Name
Int32
PS C:\> $Foo.GetType().GetProperties()
PS C:\>$Foo.Count
1

Pile Of Garbage fucked around with this message at 15:44 on Aug 17, 2019

Adbot
ADBOT LOVES YOU

Briantist
Dec 5, 2003

The Professor does not approve of your post.
Lipstick Apathy

Pile Of Garbage posted:

It's worth remembering that objects in PowerShell don't behave like you'd expect with normal OO languages which is due to the PSObject layer: https://blogs.msdn.microsoft.com/besidethepoint/2011/11/22/psobject-and-the-adapted-and-extended-type-systems-ats-and-ets/. This is why you can call the Count property of a System.Int32 struct despite it not having any properties:

code:
PS C:\> [System.Int32]$Foo = 219
PS C:\> $Foo.GetType().Name
Int32
PS C:\> $Foo.GetType().GetProperties()
PS C:\>$Foo.Count
1

While it's true that the PSObject layer (actually called Extended Type System) can cause objects to behave differently in some cases than the same object would in other .Net languages, that isn't related to the "virtual" .Count and .Length properties. Those were added in PowerShell v3 outside of that system, according to the developer who actually wrote it.

That link was written before PowerShell went open source IIRC, and he didn't mention where it's actually implemented, so I wasn't able to find it in PowerShell's source (searching for "count property" or "length property" within a bunch of source code doesn't really filter the results well), but it'd be fun to peep it.

Djimi
Jan 23, 2004

I like digital data

Zaepho posted:

I typically force results into an array by typing the variable.
code:
[array] $items=mydir.Items.Restrict($sFilter) | sort ReceivedTime -descending
This ensures I can always treat $items as an array no matter how many objects are returned.
Thanks for this ... my count wasn't really the crux of the issue. I found in documentation from Microsoft that searches aren't supported via the Outlook.application approach, so it was basically a known "cache" issue, and Microsoft states, "deal with it."
I rewrote everything to connect via EWS, and build a search collection with my parameters. It'll be good until next fall 2020, and I can move it to Graph then.

However, the bummer is now these two errors - that I've seen three times in the last week or so running my script, no particular time or reason. The FindItems has come up twice. The Bind, once. Anybody know what this issue is? (Not a PS issue I know, but you all are smart):
code:
Exception calling "FindItems" with "3" argument(s): "Mailbox move in progress. ..."
...
Exception calling "Bind" with "2" argument(s): "Mailbox move in progress. ... " 
I wasn't Moving any mailbox, what is this? Anything I can do about that issue? Catch the error and try, try again? That's not what I would like. This is a cron job. - Thanks.

nielsm
Jun 1, 2009



That looks very much like a "try again later" type error yes. Just handle it and reschedule the job, probably have a limit on number of retries.

Toast Museum
Dec 3, 2005

30% Iron Chef
Has anyone got a favorite primer on working with REST APIs and/or tips for using Invoke-RestMethod effectively?

Edit: related question: are a PowerShell session's default credentials stored in a way that can be accessed within the session, e.g. so I can feed them into something like a REST method? It's not the end of the world to prompt the user, but it'd be nice not to have to.

Toast Museum fucked around with this message at 18:41 on Aug 21, 2019

Submarine Sandpaper
May 27, 2007


Invoke-RestMethod has a flag for -usedefaultcredentials which to my understanding will act as a passthrough. You also have the -SessionVariable parameter which'll create a variable you can then use with further requests via the -webvariable parameter. TBH I've really only used the -sessionvariable and -webvariable with generic invoke-webrequests though.

favorite primer, I dunno, a lot is specific to the API i.e. you need to call any request and get a session token from the expected error's exception method.

Toast Museum
Dec 3, 2005

30% Iron Chef

Submarine Sandpaper posted:

Invoke-RestMethod has a flag for -usedefaultcredentials which to my understanding will act as a passthrough.

Yeah, that was the first thing I tried. Sadly, the API that put this cmdlet on my radar doesn't seem to understand what this parameter feeds it. I haven't really messed with REST before, so I don't know how common this is, but with this API, step 1 is authenticating to receive a JSON Web Token to include with subsequent requests. It sounds like I should have some options with how I receive that token, so I'll have to play with it some more.

mllaneza
Apr 28, 2007

Veteran, Bermuda Triangle Expeditionary Force, 1993-1952




Toast Museum posted:

Has anyone got a favorite primer on working with REST APIs and/or tips for using Invoke-RestMethod effectively?

Edit: related question: are a PowerShell session's default credentials stored in a way that can be accessed within the session, e.g. so I can feed them into something like a REST method? It's not the end of the world to prompt the user, but it'd be nice not to have to.

Have a copy of Postman installed to help build your API calls, and possibly Fiddler to see what's actually going in and out for https calls.

For the edit, I don't believe you can get at the password. The user name is accessible, but that probably isn't all of what you need. I do know from experience that the user context your script is running in is sued for remote calls that don't take a -credentials parameter. We have a lot (hundreds) of systems in an unknown or degraded state, so my management scripts use Get-Service -computerName to test if a system will listen to what we need to tell it to do. Running that as the appropriate secondary account is important.

I got to look like a wizard today. Our interns wanted a list of all the user IDs in use at all of our sites as opposed to the parent company's sites that coexist in the same domain. I had him list those off, put them in a text file, loaded that into a variable with Get-Content, and then did a get-ADUser in a foreach loop over all of our sites. Two lines. Three if you count piping all the .names to a text file.

PierreTheMime
Dec 9, 2004

Hero of hormagaunts everywhere!
Buglord

Toast Museum posted:

Yeah, that was the first thing I tried. Sadly, the API that put this cmdlet on my radar doesn't seem to understand what this parameter feeds it. I haven't really messed with REST before, so I don't know how common this is, but with this API, step 1 is authenticating to receive a JSON Web Token to include with subsequent requests. It sounds like I should have some options with how I receive that token, so I'll have to play with it some more.

Authenticating prior to making most calls is common for a lot of APIs. If you’re going to do it, it may be easier to create a service account and pass that account password into your auth call as an encrypted string, obfuscating it from the user and then limiting access by another method such as a Group membership. This also resolves issues where personal account passwords can (and should) expire, but service accounts are often given more leeway or a non-expiration exemption for continual use.

Digging into REST calls was the first time I took advantage of PowerShell v5+ classes. There’s often a lot of steps for performing work that can be kept in a separate class file so you can just start a script “Using x” and write a line or two instead of dragging things around. It’s also nice for things like auto-renewing auth tokens if they expire mid-session without creating hiccups.

Toast Museum
Dec 3, 2005

30% Iron Chef
Thanks to both of you for those replies. I've been bogged down with other poo poo and haven't had a chance to mess with that project much since I asked about it, but they gave me some good things to follow up on.

In the meantime, with the ActiveDirectory module, most of the cmdlets let you specify a DC to contact, but is there any way to indicate that certain DCs shouldn't be used? A couple of DCs on the domain I'm dealing with appear to refuse the connection request when my commands go to them. I'm not a domain admin, so my ability to diagnose issues with DCs is limited, and my ability to resolve DC issues myself is nonexistent. I'd rather not hard-code a specific DC hostname into my functions, but I can live with a blacklist or preference order or something.

PierreTheMime
Dec 9, 2004

Hero of hormagaunts everywhere!
Buglord

Toast Museum posted:

Thanks to both of you for those replies. I've been bogged down with other poo poo and haven't had a chance to mess with that project much since I asked about it, but they gave me some good things to follow up on.

In the meantime, with the ActiveDirectory module, most of the cmdlets let you specify a DC to contact, but is there any way to indicate that certain DCs shouldn't be used? A couple of DCs on the domain I'm dealing with appear to refuse the connection request when my commands go to them. I'm not a domain admin, so my ability to diagnose issues with DCs is limited, and my ability to resolve DC issues myself is nonexistent. I'd rather not hard-code a specific DC hostname into my functions, but I can live with a blacklist or preference order or something.

That's a little specific to what you're trying to do, but if you have a large list of domain controllers available you can use "Get-ADDomainController -Discover" to get a list of available hosts and then work with that. I just have the one so I don't have a very good way to create a working example.

Toast Museum
Dec 3, 2005

30% Iron Chef

PierreTheMime posted:

That's a little specific to what you're trying to do, but if you have a large list of domain controllers available you can use "Get-ADDomainController -Discover" to get a list of available hosts and then work with that. I just have the one so I don't have a very good way to create a working example.

Yeah, I've already got a list of DCs, and I've identified the ones that don't respond to my queries. Now the trick is to make sure queries don't get sent to those DCs without resorting to specifying a particular DC to use every time.

If there's not a more elegant solution, I guess I can wrap each AD command with a try/catch to catch that exception and try again with a specified DC. That way the command will go where it wants and only go with a hard-coded server as a fallback. It's not great but it's not quite putting all my eggs in one basket, and it mostly doesn't bypass whatever load balancing normally happens.

PierreTheMime
Dec 9, 2004

Hero of hormagaunts everywhere!
Buglord

Toast Museum posted:

Yeah, I've already got a list of DCs, and I've identified the ones that don't respond to my queries. Now the trick is to make sure queries don't get sent to those DCs without resorting to specifying a particular DC to use every time.

If there's not a more elegant solution, I guess I can wrap each AD command with a try/catch to catch that exception and try again with a specified DC. That way the command will go where it wants and only go with a hard-coded server as a fallback. It's not great but it's not quite putting all my eggs in one basket, and it mostly doesn't bypass whatever load balancing normally happens.

You don’t even need to hardcode a list, just have it retry in a loop until it hits a DC that’ll play nice.

Though I don’t know exactly what you’re doing, I can’t imagine a single script disrupting the load balance all that much if you specified a whitelist.

Toast Museum
Dec 3, 2005

30% Iron Chef

PierreTheMime posted:

You don’t even need to hardcode a list, just have it retry in a loop until it hits a DC that’ll play nice.

Though I don’t know exactly what you’re doing, I can’t imagine a single script disrupting the load balance all that much if you specified a whitelist.

I'm pretty sure I had Get-ADDomainController itself try to go through a problem DC and fail on me earlier, and if that's a possibility, it looks like I'll probably have to hardcode something at some point in the process. I guess that's not the end of the world; the list of DCs is short and fairly static.

I probably could have phrased it better, but as far as load balancing goes, my concern is not with bogging down a DC (I shouldn't make a dent), but instead with not wanting to wait for a server that's busy with other stuff. Again, not a dealbreaker, but nice if I can avoid it.

skipdogg
Nov 29, 2004
Resident SRT-4 Expert

I know you're not the AD admin, but something is funky if you're getting bounced around to all sorts of DC's and a couple of DC's are unresponsive.

If everything is setup properly you should only be dealing with DC's in your local site, or the assigned DC for your site.

I wouldn't worry about hard coding to a single server. It takes a ton to bog down a DC. I have ones that handle thousands of logins a minute and something like 300 ldap queries a second and they don't even blink at the load. It's not ideal though, I personally don't like it when folks hardcode to a certain server as it causes issues during DC upgrades and replacements... but we also keep the environment updated properly so we don't run into issues you're seeing.

nielsm
Jun 1, 2009



The odd thing about the ActiveDirectory module is that it uses a webservice instead of straight LDAP. We definitely have AD servers (running a separate domain) I can manage via the AD Users and Computers MMC-snapin, but can't access via the ActiveDirectory module in PowerShell.

Briantist
Dec 5, 2003

The Professor does not approve of your post.
Lipstick Apathy

Toast Museum posted:

Thanks to both of you for those replies. I've been bogged down with other poo poo and haven't had a chance to mess with that project much since I asked about it, but they gave me some good things to follow up on.

In the meantime, with the ActiveDirectory module, most of the cmdlets let you specify a DC to contact, but is there any way to indicate that certain DCs shouldn't be used? A couple of DCs on the domain I'm dealing with appear to refuse the connection request when my commands go to them. I'm not a domain admin, so my ability to diagnose issues with DCs is limited, and my ability to resolve DC issues myself is nonexistent. I'd rather not hard-code a specific DC hostname into my functions, but I can live with a blacklist or preference order or something.
The best way to do this is with Get-ADDomainController -Service ADWS -Discover -ForceDiscover.

ADWS is Active Directory Web Services. ADWS was introduced somewhere around 2008 or 2008R2, and is included by default on DCs that run that OS (regardless of functional level if memory serves). You could also download and install ADWS separately on 2003 R2 I think. So it sounds like you might have old rear end DCs in your network!

(Nearly) all of the cmdlets in the ActiveDirectory module use ADWS so this is why I use this method.

Toast Museum
Dec 3, 2005

30% Iron Chef

Briantist posted:

The best way to do this is with Get-ADDomainController -Service ADWS -Discover -ForceDiscover.

ADWS is Active Directory Web Services. ADWS was introduced somewhere around 2008 or 2008R2, and is included by default on DCs that run that OS (regardless of functional level if memory serves). You could also download and install ADWS separately on 2003 R2 I think. So it sounds like you might have old rear end DCs in your network!

(Nearly) all of the cmdlets in the ActiveDirectory module use ADWS so this is why I use this method.

That was actually the first thing I looked at today. As far as that command is concerned, all of the DCs in this site are running ADWS, including the ones that won't take my PowerShell commands. Of the five DCs in my site, the three that I can talk to are running 2012 R2, and the two that aren't working for me are running 2016.

Toast Museum
Dec 3, 2005

30% Iron Chef

Toast Museum posted:

That was actually the first thing I looked at today. As far as that command is concerned, all of the DCs in this site are running ADWS, including the ones that won't take my PowerShell commands. Of the five DCs in my site, the three that I can talk to are running 2012 R2, and the two that aren't working for me are running 2016.

In case anyone was on tenterhooks waiting to hear how this turned out, the culprit ended up being a firewall rule blocking port 9389, which is the default port for ADWS.

MF_James
May 8, 2008
I CANNOT HANDLE BEING CALLED OUT ON MY DUMBASS OPINIONS ABOUT ANTI-VIRUS AND SECURITY. I REALLY LIKE TO THINK THAT I KNOW THINGS HERE

INSTEAD I AM GOING TO WHINE ABOUT IT IN OTHER THREADS SO MY OPINION CAN FEEL VALIDATED IN AN ECHO CHAMBER I LIKE

Toast Museum posted:

That was actually the first thing I looked at today. As far as that command is concerned, all of the DCs in this site are running ADWS, including the ones that won't take my PowerShell commands. Of the five DCs in my site, the three that I can talk to are running 2012 R2, and the two that aren't working for me are running 2016.

Could it be something like you have to traverse a firewall to get to the DCs and ports aren't opened to all of them?

*edit* oh rofl read all the replies christ...

CzarChasm
Mar 14, 2009

I don't like it when you're watching me eat.
So I have a powershell script that will go into a specific exchange mailbox, read the contents and pull out pertinent information (Recipient email address, body of the message, any attachments, date and time) and it builds a text file containing this information. This worked pretty well until late last year when Microsoft implemented tighter security and required authentication and a whole host of other things that are a bit outside my expertise. I created a cumbersome workaround, but I want to get back to getting this original script certified and working again.

Is there some company or service that anyone could recommend that will help take care of the certification/authentication, even if it costs a bit of money? I don't need any help building the base program or making any changes to it really, but the cert stuff is just a complete wall for me right now.

nielsm
Jun 1, 2009



Is this going to run on a client machine through an interactive login? If it is, maybe just use Automation. Basically $outlook = New-Object -ComObject Outlook.Application and go from there.

CzarChasm
Mar 14, 2009

I don't like it when you're watching me eat.

nielsm posted:

Is this going to run on a client machine through an interactive login? If it is, maybe just use Automation. Basically $outlook = New-Object -ComObject Outlook.Application and go from there.

The program as written is set up to be run through a scheduled batch on a server where there would not be an outlook client.

Judge Schnoopy
Nov 2, 2005

dont even TRY it, pal
You'll want a password vault with an API. It can secure the password you need and provide it to your script on demand. The password vault would use your user / service account credentials from the scheduled job to authenticate and retrieve the vault password.

Dirt Road Junglist
Oct 8, 2010

We will be cruel
And through our cruelty
They will know who we are

Judge Schnoopy posted:

You'll want a password vault with an API. It can secure the password you need and provide it to your script on demand. The password vault would use your user / service account credentials from the scheduled job to authenticate and retrieve the vault password.

My company uses Hashicorp Vault for this, but we've also done some hacky poo poo with storing hashed .key files on server shares.

Vault is a much more robust option, tho, and you can even have it rotate the password automatically.

PierreTheMime
Dec 9, 2004

Hero of hormagaunts everywhere!
Buglord
Is there a better was to return the substring on a split of the second-to-last occurrence of a character than this?
code:
($Var.Split($Char)[-2,-1]) -Join($Char)
This works and I can't immediately think of something else, but this feels awkward.

Pile Of Garbage
May 28, 2007



I'm confused, maybe add some expected inputs and outputs?

That aside if you're dealing with arrays then you could pipe to Select-Object -Last 2 and then get the first element of whatever that returns.

Judge Schnoopy
Nov 2, 2005

dont even TRY it, pal

PierreTheMime posted:

Is there a better was to return the substring on a split of the second-to-last occurrence of a character than this?
code:
($Var.Split($Char)[-2,-1]) -Join($Char)
This works and I can't immediately think of something else, but this feels awkward.

I've done this numerous times. Working with raw strings instead of objects is never fun but like you said, the split - select - join works.

You can do fun transforms on the split objects before you join them back up, too. For example, you can split 192.168.1.100, replace [2] with "2", join them back into 192.168.2.100, and you don't have to deal with byte math and subnets.

PierreTheMime
Dec 9, 2004

Hero of hormagaunts everywhere!
Buglord
Anyone worked with Cloudability's API using Invoke-RestMethod? The way they pass their authentication is weird and I can't seem to figure out how to manage it. In curl they're passing their auth key as the user and a null password (like so "AUTHKEY:", using the colon to divide the blank password), which works but it's horribly documented and took me forever to figure out what they were asking for. I've tried creating a credential object and passing that as well as creating a base64-encoded pair of the auth key and a blank variable as a basic header, but both ways are giving me a 401 unauthorized error.

Toast Museum
Dec 3, 2005

30% Iron Chef

PierreTheMime posted:

Anyone worked with Cloudability's API using Invoke-RestMethod? The way they pass their authentication is weird and I can't seem to figure out how to manage it. In curl they're passing their auth key as the user and a null password (like so "AUTHKEY:", using the colon to divide the blank password), which works but it's horribly documented and took me forever to figure out what they were asking for. I've tried creating a credential object and passing that as well as creating a base64-encoded pair of the auth key and a blank variable as a basic header, but both ways are giving me a 401 unauthorized error.

I haven't messed with that API, but try a JSON object.

Edit: I had a quick look at their documentation. Looks like you're supposed to go into your account preferences, turn on the API, and receive an API key. Once you have that, you can do something like
code:
$Key = Get-Content \\Path\To\Your\API.key
$Auth = @{Authorization = "Basic $Key"} | ConvertTo-Json

Invoke-RestMethod -ContentType application/json -Headers $Auth
With the Invoke-RestMethod command's other parameters based on what you're trying to do.

Toast Museum fucked around with this message at 17:52 on Oct 24, 2019

mystes
May 31, 2006

Powershell 7 is getting a bunch of stuff: https://www.theregister.co.uk/2019/10/24/powershell_7_preview_5/

There are a lot of fancy features but honestly I think I'm happiest with the fact that Select-String will finally show where a match is in the line by inverting the colors.

CLAM DOWN
Feb 13, 2007




mystes posted:

Powershell 7 is getting a bunch of stuff: https://www.theregister.co.uk/2019/10/24/powershell_7_preview_5/

There are a lot of fancy features but honestly I think I'm happiest with the fact that Select-String will finally show where a match is in the line by inverting the colors.

Oh my god thank gently caress

quote:

Hidden away in the notes is also the return of Get-HotFix

bitterandtwisted
Sep 4, 2006




I'm trying to add all contacts from one company to a 365 distribution group. I tried a variation of something that works for mailboxes

code:
get-mailcontact | where windowsemailaddress -like *contoso.com |
>> foreach{
>> add-distributiongroupmember -identity contoso -member $_
>> }
Which comes up with error
code:
Cannot process argument transformation on parameter 'Member'. Cannot convert the "[user]" value of type "Deserialized.Microsoft.Exchange.Data.Directory.Management.MailContact"
to type "Microsoft.Exchange.Configuration.Tasks.RecipientWithAdUserGroupIdParameter`1[Microsoft.Exchange.Configuration.Tasks.RecipientIdParameter]".
    + CategoryInfo          : InvalidData: (:) [Add-DistributionGroupMember], ParameterBindin...mationException
    + FullyQualifiedErrorId : ParameterArgumentTransformationError,Add-DistributionGroupMember
    + PSComputerName        : ps.outlook.com
Google is surprisingly baron of results for contact objects

The Fool
Oct 16, 2003


The type of object created by get-mailcontact is different than the type expected by add-distributiongroupmember

You’ll need to pull the email address string out of the contact object, and then it should work as an argument for add-distributiongroupmember.

Pile Of Garbage
May 28, 2007



Christ the docs.microsoft.com site is hot garbage. Was trying to see what Get-MailContact returns and it links to this abomination which lists inputs and output return types on a single fuckin page. Also it does so in a weird table which won't render properly if your viewport is less than maybe 720px horizontal.

ANYWAY:

bitterandtwisted posted:

I'm trying to add all contacts from one company to a 365 distribution group. I tried a variation of something that works for mailboxes

code:
get-mailcontact | where windowsemailaddress -like *contoso.com |
>> foreach{
>> add-distributiongroupmember -identity contoso -member $_
>> }
Which comes up with error
code:
Cannot process argument transformation on parameter 'Member'. Cannot convert the "[user]" value of type "Deserialized.Microsoft.Exchange.Data.Directory.Management.MailContact"
to type "Microsoft.Exchange.Configuration.Tasks.RecipientWithAdUserGroupIdParameter`1[Microsoft.Exchange.Configuration.Tasks.RecipientIdParameter]".
    + CategoryInfo          : InvalidData: (:) [Add-DistributionGroupMember], ParameterBindin...mationException
    + FullyQualifiedErrorId : ParameterArgumentTransformationError,Add-DistributionGroupMember
    + PSComputerName        : ps.outlook.com
Google is surprisingly baron of results for contact objects

The Get-MailContact cmdlet returns a MailContact object which has the following properties: https://docs.microsoft.com/en-us/previous-versions/office/exchange-server-api/ff339613(v=exchg.150)#properties. Of those windowsemailaddress is not one however I believe you're looking for ExternalEmailAddress. So the quick fix here is to filter on that property and then specify it in the ForEach-Object block like so:

code:
Get-MailContact | Where-Object -FilterScript { $_.ExternalEmailAddress -like '*contoso.com' } | ForEach-Object { Add-DistributionGroupMember -Identity 'contoso' -Member $_.ExternalEmailAddress }
That isn't particularly efficient of course so you might want to look into whether you can just compile an array of contacts and then add them to the group in one. Also IIRC using the Filter parameter with Get-MailContact is faster than just getting all contacts and piping to Where-Object.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
I'm getting some strange behavior trying to detect paths in PowerShell. I have a Python script that makes a directory when started up. I have a PowerShell script waiting up to 31 seconds for that directory to be created, testing it every 10 seconds. The script should create the directory immediately; it's not very sophisticated and very little is happening before it's created. Still, sometimes the PowerShell script won't find it.

This is being run in vagrant over a series of provisioning scripts. When I ran the PowerShell code in the same script that launched the Python script, the failure rate was over 50%. I moved it to a subsequent file on some notion that maybe a new process space would shut it up. It did... for about a month. It decided to celebrate Halloween by rising from the grave.

I've had the Python script test the path afterwards and be fine. I'm now having it print the contents of the parent directory just to doubly-prove it. The polling mechanism is showing time stamps of checks so it's not like it's just blowing through the checks and then timing out.

Edit: I guess I should mention I'm just working with Test-Path here.

The Fool
Oct 16, 2003


Maybe try the .net method?

My brief super scientific test showed no difference between the two, but it's worth a shot.

code:
[System.IO.Directory]::Exists("C:\testpath")

Toast Museum
Dec 3, 2005

30% Iron Chef
Is PowerShell throwing any errors? Is there any pattern to when it behaves unexpectedly?

Without knowing how your scripts interact, and hoping for low-hanging fruit, my first guess is that the path isn't enclosed in quotes, and test-path is choking when given a path with a space in it.

Can you show us some of the code?

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
It's a snippet.
code:
# We need C:\vagrant\trampoline\in to exist. Try $retries times, waiting a second each time, to get that file
$retries = 0
$trampoline_check_start = Get-Date -Format "dddd MM/dd/yyyy HH:mm:ss.fffffff"
Write-Host "Starting checks at $($trampoline_check_start)"
while((Test-Path C:\vagrant\trampoline\in) -eq $false -and $retries -lt $trampoline_retries) {
    $new_retries = $retries + 1
    Start-Sleep -Seconds 1
    Set-Variable -Name "retries" -Value $new_retries

    # Try relaunching every 10 seconds. I've seen it just kind of flash by before so it doesn't mess up starting up
    # sometimes.
    If($new_retries % 10 -eq 0)
    {
        # Let's see what we have in C:\vagrant
        Get-ChildItem C:\vagrant -Recurse | Write-Host
        Start-ScheduledTask -TaskName "REDACTED" -ErrorAction Stop
    }
}

if($retries -ge $trampoline_retries) {
    throw "C:\vagrant\trampoline\in was never created within $retries seconds of starting the trampoline script"
}
It will end up throwing the exception at the end even now that it's in a separate script launched after the previous one. Note that the Python script is being run as a scheduled task so it continues in the background. I imagine that causes some shenanigans too. I'm having it try to restart the task if it doesn't find it. Some things I know from that:

1. My success rate went waaaaaay up moving the check to a separate script launched afterwards.
2. Trying to pause between launching the Python script and monitoring for the file didn't help at all. I had it wait upwards of 5 minutes when they were in the same script and the error rate didn't change.

Adbot
ADBOT LOVES YOU

Toast Museum
Dec 3, 2005

30% Iron Chef

Rocko Bonaparte posted:

It's a snippet.
code:
# We need C:\vagrant\trampoline\in to exist. Try $retries times, waiting a second each time, to get that file
$retries = 0
$trampoline_check_start = Get-Date -Format "dddd MM/dd/yyyy HH:mm:ss.fffffff"
Write-Host "Starting checks at $($trampoline_check_start)"
while((Test-Path C:\vagrant\trampoline\in) -eq $false -and $retries -lt $trampoline_retries) {
    $new_retries = $retries + 1
    Start-Sleep -Seconds 1
    Set-Variable -Name "retries" -Value $new_retries

    # Try relaunching every 10 seconds. I've seen it just kind of flash by before so it doesn't mess up starting up
    # sometimes.
    If($new_retries % 10 -eq 0)
    {
        # Let's see what we have in C:\vagrant
        Get-ChildItem C:\vagrant -Recurse | Write-Host
        Start-ScheduledTask -TaskName "REDACTED" -ErrorAction Stop
    }
}

if($retries -ge $trampoline_retries) {
    throw "C:\vagrant\trampoline\in was never created within $retries seconds of starting the trampoline script"
}
It will end up throwing the exception at the end even now that it's in a separate script launched after the previous one. Note that the Python script is being run as a scheduled task so it continues in the background. I imagine that causes some shenanigans too. I'm having it try to restart the task if it doesn't find it. Some things I know from that:

1. My success rate went waaaaaay up moving the check to a separate script launched afterwards.
2. Trying to pause between launching the Python script and monitoring for the file didn't help at all. I had it wait upwards of 5 minutes when they were in the same script and the error rate didn't change.

A smoking gun isn't jumping out at me, but some things come to mind.
  • Is $trampoline_retries defined elsewhere in the script? I assume so, because otherwise that error would get thrown every time.
  • Does the Python script in that scheduled task run another instance of this script, or is there another script handling those interactions?
  • Are the paths actually hardcoded as shown here, or are they variables defined elsewhere in the script(s)?
  • Get-ChildItem C:\DoesNotExist -recurse gives unusual results and takes a long time to run (about 80 seconds on the computer I tried it on). If PowerShell can't see the path for some reason, Get-ChildItem -LiteralPath "c:\vagrant" -recurse will give more useful results, and it's probably preferable to be explicit about parameters in a script anyway.
  • Why do
    code:
        $new_retries = $retries + 1
        Set-Variable -Name "retries" -Value $new_retries
    
    rather than $Retries += 1
  • Piping to Write-Host shouldn't be necessary, and both uses of Write-Host here are probably better off as Write-Verbose.

I hope this doesn't come off as nitpicky; I'm hoping that squaring away some of the tangential stuff will help clarify what's causing the issue at hand.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply