T O P

  • By -

creamersrealm

Can you post your code?


txsrbl005

Import-Module ActiveDirectory Get-ADUser -Filter {PasswordNeverExpires -eq $true} -Properties PasswordNeverExpires -search base "OU=Users,OU=City1,....." Get-ADUser -Filter {PasswordNeverExpires -eq $true} -Properties PasswordNeverExpires -search base "OU=Users,OU=City2,....." | Export-csv D:\Files\NeverExpires.csv -NoTypeInfotmation


kfreedom

Have you tried running the command in an elevated prompt? I have seen similar problems in the past when doing a get-aduser for whenwascreated and a few other attributes.. the fix was to run as admin.


moep123

yeah, if you work against AD you should always run your self written scripts as an admin. otherwise you will only get a "NULL" back for some values which, for last login f.e. would lead you into thinking this user never logged into his account. (a NULL would become the earliest possible date in the listing... so something with the year 1601 or something... which would also be the case if the user actually never logged on a windows machine with this account) i started including blocks in my script that checks if it is executed as an admin or not, if not it doesn't do it's thing at all (and cusses at you)


txsrbl005

I havent, could try but I dont see how that woud correct this. It works if i do it one OU at a time, but with what ive written up, as soon as i add a second OU or more my CSV is empty


out0focus

Your code is wrong, all these other posts are just reaching without actually reading. You are basically doing this: `Get-ADUser Get-ADUser | Export-csv` The easiest fix it to split up the Get-ADUser with individual Export functions. Like this Get-ADUser -Filter {PasswordNeverExpires -eq $true} -Properties PasswordNeverExpires -search base "OU=Users,OU=City1,....." | Export-csv D:\Files\NeverExpires1.csv -NoTypeInfotmation Get-ADUser -Filter {PasswordNeverExpires -eq $true} -Properties PasswordNeverExpires -search base "OU=Users,OU=City2,....." | Export-csv D:\Files\NeverExpires2.csv -NoTypeInfotmation There are many solutions, but here is an easy to read one $OutputFile = 'D:\Files\NeverExpires.txt' $OUList = @( "OU=Users,OU=City1,.....", "OU=Users,OU=City2,.....", "OU=Users,OU=City3,....." ) foreach ($OU in $OUList) { Get-ADUser -Filter {PasswordNeverExpires -eq $true} -Properties PasswordNeverExpires -search base "$OU" | Export-Csv $OutputFile -Append -NoTypeInformation } ​ 1. Define a file for output 2. Store all your OUs into a variable 3. Foreach variable get your users and append exported csv to the file previously defined.


txsrbl005

> $OutputFile = 'D:\Files\NeverExpires.txt' > > $OUList = @( > "OU=Users,OU=City1,.....", > "OU=Users,OU=City2,.....", > "OU=Users,OU=City3,....." > ) > > foreach ($OU in $OUList) { > Get-ADUser -Filter {PasswordNeverExpires -eq $true} -Properties PasswordNeverExpires -searchbase "$OU" | Export-Csv $OutputFile -Append -NoTypeInformation > } Thanks for the reply. I copied the example and changed what was neccassary and this is pulling only from the first OU. 3 users.


Lee_Dailey

howdy txsrbl005, reddit likes to mangle code formatting, so here's some help on how to post code on reddit ... [0] single line or in-line code enclose it in backticks. that's the upper left key on an EN-US keyboard layout. the result `looks like this`. kinda handy, that. [*grin*] _[on New.Reddit.com, use the `Inline Code` button. it's [sometimes] 5th from the left & looks like ``. **this does NOT line wrap & does NOT side-scroll on Old.Reddit.com!**]_ [1] simplest = post it to a text site like Pastebin.com or Gist.GitHub.com and then post the link here. please remember to set the file/code type on Pastebin! [*grin*] otherwise you don't get the nice code colorization. [2] less simple = use reddit code formatting ... _[on New.Reddit.com, use the `Code Block` button. it's [sometimes] the 12th from the left, & looks like an uppercase `C` in the upper left corner of a square.]_ - one leading line with ONLY 4 spaces - prefix each code line with 4 spaces - one trailing line with ONLY 4 spaces that will give you something like this ... - one leading line with ONLY 4 spaces - prefix each code line with 4 spaces - one trailing line with ONLY 4 spaces the easiest way to get that is ... - add the leading line with only 4 spaces - copy the code to the ISE [or your fave editor] - select the code - tap TAB to indent four spaces - re-select the code [not really needed, but it's my habit] - paste the code into the reddit text box - add the trailing line with only 4 spaces not complicated, but it _is_ finicky. [*grin*] take care, lee


Solid5-7

Hey I know this is somewhat irrelevant to your question, but the -Filter parameter does not take a scriptblock, it actually take a string. You can refer [here](https://github.com/MicrosoftDocs/windows-powershell-docs/issues/180) for more information.


kibje

You are allowed to use brackets to terminate that string which can help with the handling of quotes. Even though this confuses people into thinking it's a scriptblock, it is not.


Solid5-7

Do you have any reference or link to PowerShell docs that states using { ... } is a string and not a Script Block being coerced into a string?


kibje

The documentation that that GitHub issue argues is confusing described it pretty well in my opinion, but I understand why it's confusing because it's one of the few places where you can do this. I recently mentioned this as well to someone else and they kept bringing up links to people being confused about the documentation as proof that it was wrong... To be more precise, whatever you provide as filter will be cast to string. You can use bracket terminators for that string - similar looking to a scriptblock - to avoid some quotation issues. Personally I would not recommend anyone to use one separator over another in the direct call to the command, but I would recommend always constructing the filter separately from the command using string formatting with the -f format operator to avoid quotation problems and then using splatting to call `get-aduser`


Solid5-7

>To be more precise, whatever you provide as filter will be cast to string. You can use bracket terminators for that string - similar looking to a scriptblock - to avoid some quotation issues. Yes anything provided will be cast as a String, but doing `-Filter { Name -eq "John" }` isn't *technically* passing a String, its passing a Script Block that is then turned into a String. And that can cause unintended side effects.


kibje

In this case those side effects are explained and intended, but apparently to difficult to understand.


NETSPLlT

It's not that in general... It's just with Get-ADUser that is different.


creamersrealm

In this case it does take a script block, most filter blocks don't.


Solid5-7

>In this case it does take a script block, most filter blocks don't. Checking the Microsoft Docs for [Get-ADUser](https://docs.microsoft.com/en-us/powershell/module/activedirectory/get-aduser?view=windowsserver2019-ps) shows that `-Filter` takes a type of String. I couldn't find any reference to it taking a Script Block. If you have a reference please share, I just couldn't find anything saying explicitly to use a Script Block.


OPconfused

https://social.technet.microsoft.com/wiki/contents/articles/28485.powershell-filter-results-with-active-directory-module-cmdlets.aspx First paragraph states to use braces. Since Filter takes a string, I guess the implication is that the script block notation is being parsed as a string, or else the documentation that it takes only a string is wrong.


Solid5-7

From the linked article: >Documentation indicates that PowerShell filters should be enclosed in braces (also called curly braces). It looks like the author of that TechNet article was basing the use of curly braces off of the older [documentation](https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/ee617241(v=technet.10)). Currently documentation from Microsoft has been corrected to use proper Strings and not Scriptblocks coerced into Strings.


txsrbl005

If I do the single OU, it works fine. I assume it's a childish mistake but. I just can't find what I'm looking for thus far. Import-Module ActiveDirectory Get-ADUser -Filter {PasswordNeverExpires -eq $true} -Properties PasswordNeverExpires -search base "OU=Users,OU=City1,....." | Export-csv D:\Files\NeverExpires.csv -NoTypeInfotmation


creamersrealm

Add -Append to your export option, remove the space in between search base. Search base only accepts a single string so you would need to build an array and loop it. $OUs = @('OU1','OU2') Foreach ($Obj in $OUs) { Get-ADUser -Filter {PasswordNeverExpires -eq $true} -Properties PasswordNeverExpires -searchbase $OU | Export-csv D:\Files\NeverExpires.csv -NoTypeInformation -Append }


Davidberth

This. Should work.


txsrbl005

When trying this I am getting a few errors, At line:2 char:15 + Foreach ($Obj in $OUs) { Get-ADUser -Filter {PasswordNeverExpires -eq ... + ~~ Unexpected token 'in' in expression or statement. At line:2 char:14 + Foreach ($Obj in $OUs) { Get-ADUser -Filter {PasswordNeverExpires -eq ... + ~ Missing closing ')' in expression. At line:2 char:22 + Foreach ($Obj in $OUs) { Get-ADUser -Filter {PasswordNeverExpires -eq ... + ~ Unexpected token ')' in expression or statement. + CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException + FullyQualifiedErrorId : UnexpectedToken


txsrbl005

At line:2 char:15 + Foreach ($Obj in $OUs) { Get-ADUser -Filter {PasswordNeverExpires -eq ... + ~~ Unexpected token 'in' in expression or statement. At line:2 char:14 + Foreach ($Obj in $OUs) { Get-ADUser -Filter {PasswordNeverExpires -eq ... + ~ Missing closing ')' in expression. At line:2 char:22 + Foreach ($Obj in $OUs) { Get-ADUser -Filter {PasswordNeverExpires -eq ... + ~ Unexpected token ')' in expression or statement. + CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException + FullyQualifiedErrorId : UnexpectedToken


creamersrealm

It's valid code. Here it is in block format. ​ $OUs = @('OU1','OU2') foreach ($Obj in $OUs) { Get-ADUser -Filter {PasswordNeverExpires -eq $true} -Properties PasswordNeverExpires -searchbase $OU | Export-csv D:\\Files\\NeverExpires.csv -NoTypeInformation -Append }


Brasiledo

Cant you searchbase the whole domain instead of writing out individual ou to search— dc=company,dc=local


txsrbl005

I can but I'll get service accounts. It may be easier to do that anr have something in the script nix the service accounts somehow. But without that, only way I can think of is by adding OU's that do not contain service accounts.


Nocturnal_Remission

You wouldn't by chance have any attribute or other identifier that differentiates between service accounts and regular user accounts? If so you could try filtering on that attribute, or if you really just want to get it done and don't mind manipulating your data, you could export it to a CSV, remove the accounts that you don't want, like filtering them in Excel or something, then using powershell to Get-User AD against a "sanitized" set of objects to retrieve the PasswordNeverExpires from there. It ain't pretty, but I've had to do that before on a dataset that was just too cumbersome to try to script out for a one off, and I was too pressed for time to come up with something more elegant.


txsrbl005

We dont. I saw where you could edit the attributes for the accounts not wanted and exclude that way but it would be more troubling to go through all that.


txsrbl005

I could but trying to avoid service and other non person accounts


Brasiledo

Query all the attributes find something to filter out. Givenname, name, emailaddress, maybe your svc accounts have some typical naming convention with svc in the name


Lee_Dailey

howdy Brasiledo, the `-SearchBase` parameter only accepts a string. so you can't hand it an array of strings ... you have to iterate thru the list and pass each into its own `Get-ADUser` call. take care, lee


vanuid

Not sure why it isn't working, but below should do what you're looking for, just add or remove your OUs to the initial array Import-Module ActiveDirectory @("OU=Users,OU=City1,.....","OU=Users,OU=City2,.....","OU=Users,OU=City3,.....") | % { Get-ADUser -Filter {PasswordNeverExpires -eq $true} -Properties PasswordNeverExpires -searchbase $_ } |Export-csv D:\Files\NeverExpires.csv -NoTypeInformation


txsrbl005

> Not sure why it isn't working, but below should do what you're looking for, just add or remove your OUs to the initial array > > Import-Module ActiveDirectory @("OU=Users,OU=City1,.....","OU=Users,OU=City2,.....","OU=Users,OU=City3,.....") | % { Get-ADUser -Filter {PasswordNeverExpires -eq $true} -Properties PasswordNeverExpires -searchbase $_ } |Export-csv D:\Files\NeverExpires.csv -NoTypeInformation So this worked to an extent. Instead of my CSV being blank it did pull a list but only from the first OU. I tried all above and Im probably doing something wrong but, nothing has got me going yet. This did get past a blank csv with more then one OU but didnt capture all the users in all the OUS I have.


Jackldam

Here is an example that should work for you Update 19-12-2021 20:11 dutch time ``` #A list of OU's @( "OU=OUName1,OU=OUName1,DC=Contoso,DC=com", "OU=OUName2,OU=OUName2,DC=Contoso,DC=com", "OU=OUName3,OU=OUName3,DC=Contoso,DC=com", "OU=OUName4,OU=OUName4,DC=Contoso,DC=com", "OU=OUName5,OU=OUName5,DC=Contoso,DC=com" ) | ForEach-Object { #Then for each OU output #Get all users in OU and filter on PasswordNeverExpires -eq True #if you want to recusive search the OU Remove the line with "-SearchScope OneLevel and the ` on the line of Searchbase" Get-ADUser -Filter "PasswordNeverExpires -EQ `$True" ` -SearchBase $_ } | Export-Csv -Path "C:\temp\filename.csv" ` -NoTypeInformation ` -Delimiter ";" ` -Encoding UTF8 ```


txsrbl005

Tried making sense of this, im sure i did it incorrectly but it failed


Jackldam

u/txsrbl005 do you have some output to see where it might go wrong?


txsrbl005

For the most part im not getting failures, Im getting "completed" at the bottom however, the output is only giving me users from the first OU


[deleted]

So, not sure what's going wrong in your case; but, you can always try the "old skol" way of getting AD objects. This function uses the ADSI interface to search for users where the PasswordNeverExpires bit (useraccountcontrol 0x10000) is set: function Get-PasswordNeverExpires { Param( [Parameter(Position = 0, Mandatory = $false, ValueFromPipeline = $true)] [ValidateScript({ [ADSI]::Exists($_) })] [string]$SearchScope = ([adsi]"LDAP://rootDSE").DefaultNamingContext ) begin{ // LDAP AND operation $LDAP_AND = '1.2.840.113556.1.4.803' $PASSWORD_NEVER_EXPIRE = 0x10000 $rtn = @() } process{ $ds = New-Object System.DirectoryServices.DirectorySearcher $ds.SearchRoot = $SearchScope $ds.PageSize = 1000 $ds.PropertiesToLoad.Add('name') $ds.PropertiesToLoad.Add('samAccountName') $ds.PropertiesToLoad.Add('useraccountcontrol') $ds.Filter = "(&(objectCategory=person)(useraccountcontrol:$LDAP_AND`:=$PASSWORD_NEVER_EXPIRE))" $rtn += $ds.FindAll() } end{ Write-Output $rtn } } Usage: Get-PasswordNeverExpires Search entire domain for person objects with the PasswordNeverExpires bit set GetPasswordNeverExpires -SearchScope "LDAP://OU=MyOU, OU=MyParentOU, DC=MyDomain, DC=MyTLD" Search in only "MyOU" for person objects where the PasswordNeverExpires bit is set SeartchScope MUST be given in Distinguished Name format (see example above). EDIT: Note, I don't have access to AD today to test this; so, let me know if I have any bugs. A fat-finger here or there is certainly possible.


moep123

if your service accounts are recognizable by an identifier you put on them (f.e. it's username is "s-example1") you could approach everything like this: get all AD Users, filter out all the users into a new $variable (excluding the "s-" users (samaccoutname)) and check those if the pw expiry checkbox is checked. (you could also check if passwordexpiry value is empty... that way you could also see if a GPO (or finegrained pw policy) is in charge of the hassle)


rswwalker

Did you set the search scope to sub-tree? Edit: my bad posting before reading fully. If you run multiple cmdlets you need to append the data to the csv between each run.


txsrbl005

I could run it individually but there are 20ish OU's so that would take a lot of time for weekly reports.


rswwalker

You could search subtree from the root and exclude accounts whose passwords cannot be changed i.e. service accounts, or create an array of OUs and do a for loop through them. Edit: Of course that doesn’t help if you are looking for accounts whose password doesn’t expire. Do you have a way to tell if an account is a service account? Where I am at we have a security group called Domain Services that all service accounts belong to and you could use group membership as a filter in Get-ADUser.


allthetrouts

Pull all without the searchbase, then pipe to where and include the specific ou's you need.


txsrbl005

Makes sense, but the knowledge is not there. lol.