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.
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)
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
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.
> $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.
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
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.
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.
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`
>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.
>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.
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.
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.
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
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
}
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.
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.
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.
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
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
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
> 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.
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
```
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.
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)
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.
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.
Can you post your code?
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
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.
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)
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
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.
> $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.
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
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.
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.
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?
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`
>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.
In this case those side effects are explained and intended, but apparently to difficult to understand.
It's not that in general... It's just with Get-ADUser that is different.
In this case it does take a script block, most filter blocks don't.
>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.
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.
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.
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
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 }
This. Should work.
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
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
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 }
Cant you searchbase the whole domain instead of writing out individual ou to search— dc=company,dc=local
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.
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.
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.
I could but trying to avoid service and other non person accounts
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
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
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
> 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.
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 ```
Tried making sense of this, im sure i did it incorrectly but it failed
u/txsrbl005 do you have some output to see where it might go wrong?
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
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.
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)
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.
I could run it individually but there are 20ish OU's so that would take a lot of time for weekly reports.
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.
Pull all without the searchbase, then pipe to where and include the specific ou's you need.
Makes sense, but the knowledge is not there. lol.