├── .github └── FUNDING.yml ├── .gitignore ├── Build └── Manage-Module.ps1 ├── CHANGELOG.MD ├── Docs ├── Find-Password.md ├── Find-PasswordNotification.md ├── Find-PasswordQuality.md ├── Images │ ├── PasswordQuality1.png │ ├── PasswordQuality2.png │ ├── PasswordQuality3.png │ ├── PasswordQuality4.png │ ├── PasswordQuality5.png │ └── PasswordQuality6.png ├── New-PasswordConfigurationEmail.md ├── New-PasswordConfigurationOption.md ├── New-PasswordConfigurationReport.md ├── New-PasswordConfigurationRule.md ├── New-PasswordConfigurationRuleReminder.md ├── New-PasswordConfigurationTemplate.md ├── New-PasswordConfigurationType.md ├── Readme.md ├── Show-PasswordQuality.md └── Start-PasswordSolution.md ├── Examples ├── Example-EncryptPassword.ps1 ├── Example-FindUsers.ps1 ├── Example-FindUsersAdvanced.ps1 ├── Example-FindUsersEntra.ps1 ├── Example-FindUsersPasswordQualityConsole.ps1 ├── Example-FindUsersPasswordQualityConsoleWithReplacements.ps1 ├── Example-FindUsersPasswordQualityReport.ps1 ├── Example-FindUsersPasswordQualityReportWithReplacements.ps1 ├── Example-PasswordDashboard.ps1 ├── Example-PasswordSolution-Entra.ps1 ├── Example-PasswordSolution-LegacyConfiguration01.ps1 ├── Example-PasswordSolution-ModernConfiguration01.ps1 ├── Example-ReRegisterTaskAsGMSA.ps1 ├── Example-SearchNotification.ps1 └── WeakPasswordGenerator.ps1 ├── PasswordSolution.psd1 ├── PasswordSolution.psm1 ├── Private ├── Add-ManagerInformation.ps1 ├── Add-ParametersToString.ps1 ├── Export-SearchInformation.ps1 ├── Fromat-ReminderDays.ps1 ├── Import-SearchInformation.ps1 ├── Invoke-PasswordRuleProcessing.ps1 ├── New-HTMLReport.ps1 ├── Send-PasswordAdminNotifications.ps1 ├── Send-PasswordEmail.ps1 ├── Send-PasswordManagerNotifications.ps1 ├── Send-PasswordSecurityNotifications.ps1 ├── Send-PasswordUserNotifications.ps1 └── Set-PasswordConfiguration.ps1 ├── Public ├── Find-Password.ps1 ├── Find-PasswordEntra.ps1 ├── Find-PasswordNotification.ps1 ├── Find-PasswordQuality.ps1 ├── New-PasswordConfigurationEmail.ps1 ├── New-PasswordConfigurationEntra.ps1 ├── New-PasswordConfigurationExternalUsers.ps1 ├── New-PasswordConfigurationOption.ps1 ├── New-PasswordConfigurationReplacement.ps1 ├── New-PasswordConfigurationReport.ps1 ├── New-PasswordConfigurationRule.ps1 ├── New-PasswordConfigurationRuleReminder.ps1 ├── New-PasswordConfigurationTemplate.ps1 ├── New-PasswordConfigurationType.ps1 ├── Show-PasswordQuality.ps1 └── Start-PasswordSolution.ps1 └── README.MD /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: PrzemyslawKlys 2 | custom: https://paypal.me/PrzemyslawKlys -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Ignore/* 2 | .vs/* 3 | .vscode/* 4 | *.html 5 | *.log 6 | *.xml 7 | Artefacts/* -------------------------------------------------------------------------------- /Build/Manage-Module.ps1: -------------------------------------------------------------------------------- 1 | Clear-Host 2 | Import-Module "PSPublishModule" -Force 3 | 4 | Invoke-ModuleBuild -ModuleName 'PasswordSolution' { 5 | # Usual defaults as per standard module 6 | $Manifest = [ordered] @{ 7 | # Version number of this module. 8 | ModuleVersion = '2.1.X' 9 | # Supported PSEditions 10 | CompatiblePSEditions = @('Desktop', 'Core') 11 | 12 | PowerShellVersion = '5.1' 13 | # ID used to uniquely identify this module 14 | GUID = 'c58ff818-1de6-4500-961c-a243c2043255' 15 | # Author of this module 16 | Author = 'Przemyslaw Klys' 17 | # Company or vendor of this module 18 | CompanyName = 'Evotec' 19 | # Copyright statement for this module 20 | Copyright = "(c) 2011 - $((Get-Date).Year) Przemyslaw Klys @ Evotec. All rights reserved." 21 | # Description of the functionality provided by this module 22 | Description = "This module allows the creation of password expiry emails for users, managers, administrators, and security according to defined templates. It's able to work with different rules allowing to fully customize who gets the email and when." 23 | # Minimum version of the Windows PowerShell engine required by this module 24 | Tags = 'password', 'passwordexpiry', 'activedirectory', 'windows' 25 | # A URL to the main website for this project. 26 | ProjectUri = 'https://github.com/EvotecIT/PasswordSolution' 27 | 28 | # A URL to an icon representing this module. 29 | IconUri = 'https://evotec.xyz/wp-content/uploads/2022/08/PasswordSolution.png' 30 | } 31 | New-ConfigurationManifest @Manifest 32 | 33 | New-ConfigurationModule -Type RequiredModule -Name 'PSSharedGoods', 'PSWriteHTML', 'PSWriteColor' -Guid Auto -Version Latest 34 | New-ConfigurationModule -Type RequiredModule -Name 'Mailozaurr' -Guid Auto -Version 1.0.0 35 | New-ConfigurationModule -Type ExternalModule -Name @( 36 | #"Microsoft.PowerShell.Management" 37 | #"Microsoft.PowerShell.Utility" 38 | "ActiveDirectory" 39 | ) 40 | New-ConfigurationModule -Type ApprovedModule -Name 'PSSharedGoods', 'PSWriteColor', 'Connectimo', 'PSUnifi', 'PSWebToolbox', 'PSMyPassword' 41 | 42 | New-ConfigurationModuleSkip -IgnoreModuleName @( 43 | # Default modules 44 | 'NetTCPIP' 45 | 'Microsoft.WSMan.Management' 46 | # Password Quality 47 | 'DSInternals' 48 | # Graph Cmdlets 49 | 'Microsoft.Graph.Users' 50 | 'Microsoft.Graph.Identity.DirectoryManagement' 51 | ) -IgnoreFunctionName @( 52 | 'Select-Unique', 'Compare-TwoArrays' # those functions are internal within private function 53 | # Password Quality 54 | 'Get-ADReplAccount' 55 | 'Test-PasswordQuality' 56 | # Graph Cmdlets 57 | 'Get-MgDomain' 58 | 'Get-MgUser' 59 | ) 60 | 61 | $ConfigurationFormat = [ordered] @{ 62 | RemoveComments = $true 63 | RemoveEmptyLines = $true 64 | 65 | PlaceOpenBraceEnable = $true 66 | PlaceOpenBraceOnSameLine = $true 67 | PlaceOpenBraceNewLineAfter = $true 68 | PlaceOpenBraceIgnoreOneLineBlock = $false 69 | 70 | PlaceCloseBraceEnable = $true 71 | PlaceCloseBraceNewLineAfter = $false 72 | PlaceCloseBraceIgnoreOneLineBlock = $true 73 | PlaceCloseBraceNoEmptyLineBefore = $false 74 | 75 | UseConsistentIndentationEnable = $true 76 | UseConsistentIndentationKind = 'space' 77 | UseConsistentIndentationPipelineIndentation = 'IncreaseIndentationAfterEveryPipeline' 78 | UseConsistentIndentationIndentationSize = 4 79 | 80 | UseConsistentWhitespaceEnable = $true 81 | UseConsistentWhitespaceCheckInnerBrace = $true 82 | UseConsistentWhitespaceCheckOpenBrace = $true 83 | UseConsistentWhitespaceCheckOpenParen = $true 84 | UseConsistentWhitespaceCheckOperator = $true 85 | UseConsistentWhitespaceCheckPipe = $true 86 | UseConsistentWhitespaceCheckSeparator = $true 87 | 88 | AlignAssignmentStatementEnable = $true 89 | AlignAssignmentStatementCheckHashtable = $true 90 | 91 | UseCorrectCasingEnable = $true 92 | } 93 | # format PSD1 and PSM1 files when merging into a single file 94 | # enable formatting is not required as Configuration is provided 95 | New-ConfigurationFormat -ApplyTo 'OnMergePSM1', 'OnMergePSD1' -Sort None @ConfigurationFormat 96 | # format PSD1 and PSM1 files within the module 97 | # enable formatting is required to make sure that formatting is applied (with default settings) 98 | New-ConfigurationFormat -ApplyTo 'DefaultPSD1', 'DefaultPSM1' -EnableFormatting -Sort None 99 | # when creating PSD1 use special style without comments and with only required parameters 100 | New-ConfigurationFormat -ApplyTo 'DefaultPSD1', 'OnMergePSD1' -PSD1Style 'Minimal' 101 | # configuration for documentation, at the same time it enables documentation processing 102 | New-ConfigurationDocumentation -Enable:$false -StartClean -UpdateWhenNew -PathReadme 'Docs\Readme.md' -Path 'Docs' 103 | 104 | New-ConfigurationImportModule -ImportSelf 105 | 106 | New-ConfigurationBuild -Enable:$true -SignModule -MergeModuleOnBuild -MergeFunctionsFromApprovedModules -CertificateThumbprint '483292C9E317AA13B07BB7A96AE9D1A5ED9E7703' 107 | 108 | #New-ConfigurationTest -TestsPath "$PSScriptRoot\..\Tests" -Enable 109 | 110 | New-ConfigurationArtefact -Type Unpacked -Enable -Path "$PSScriptRoot\..\Artefacts\Unpacked" -AddRequiredModules 111 | New-ConfigurationArtefact -Type Packed -Enable -Path "$PSScriptRoot\..\Artefacts\Packed" -ArtefactName '.v.zip' 112 | 113 | # options for publishing to github/psgallery 114 | #New-ConfigurationPublish -Type PowerShellGallery -FilePath 'C:\Support\Important\PowerShellGalleryAPI.txt' -Enabled:$true 115 | #New-ConfigurationPublish -Type GitHub -FilePath 'C:\Support\Important\GitHubAPI.txt' -UserName 'EvotecIT' -Enabled:$true 116 | } -ExitCode -------------------------------------------------------------------------------- /CHANGELOG.MD: -------------------------------------------------------------------------------- 1 | # PasswordSolution Release History 2 | 3 | ## 2.0.3 4 | - Fix formatting in HTML output 5 | - Fix deletion of log files/reports to specific extensions only [#24](https://github.com/EvotecIT/PasswordSolution/issues/24) 6 | 7 | ## 2.0.2 - 2024.10.01 8 | - Add `SearchBase` when managers are not required, or the scope for both users and managers is very limited (use FilterOrganizationalUnit instead) 9 | 10 | ## 2.0.1 - 2024.09.30 11 | - Fixes `AdminSection` not working properly for modern configuration 12 | - Improve error reporting when sending emails 13 | 14 | ## 2.0.0 - 2024.09.08 - **EXPERIMENTAL** 15 | - Added basic support for Microsoft Entra ID (Azure AD) 16 | - Improved reporting allowing to exclude properties from HTML reports to make them smaller 17 | - Hide **'Manager', 'ManagerDN', 'MemberOf'** by default in HTML reports (zero out ExcludeProperties to get rid of this behavior) 18 | - Made `ScrollX` default to `true` in HTML reports to make them more readable 19 | 20 | ## 1.3.2 - 2024.08.23 21 | - Improvement on logging 22 | - Small improvement to message 23 | 24 | ## 1.3.1 - 2024.08.23 25 | - Fixes `FilterOrganizationalUnit` not working properly 26 | - Add more logs 27 | 28 | ## 1.3.0 - 2024.08.23 29 | - Added `NotifyOnUserMatchingRuleForManager`, `NotifyOnUserMatchingRuleForManagerButNotCompliant` to `New-PasswordConfigurationOption` to allow for more granular control over logging 30 | - Fixes sending emails to managers based on weekdays when using modern setup configuration 31 | - Added additional check logic to prevent wrong rules configuration 32 | 33 | ## 1.2.9 - 2024.08.22 34 | - Fixes logging functionality when using modern settings 35 | 36 | ## 1.2.8 - 2024.08.22 37 | - Added `FilterOrganizationalUnit` to `New-PasswordConfigurationOption` to allow for filtering users based on OrganizationalUnit 38 | This speeds up the process of scanning users, and allows for more granular control over which users are scanned without having to go thru all users in the domain 39 | The module still gets all the users but only processes the ones that match the filter 40 | - Improved console colors a bit, to prevent some colors from being invisible 41 | 42 | ```powershell 43 | $Options = @{ 44 | # Logging to file and to screen 45 | ShowTime = $true 46 | LogFile = "$PSScriptRoot\Logs\PasswordSolution_$(($Date).ToString('yyyy-MM-dd_HH_mm_ss')).log" 47 | TimeFormat = "yyyy-MM-dd HH:mm:ss" 48 | LogMaximum = 365 49 | NotifyOnSkipUserManagerOnly = $false 50 | NotifyOnSecuritySend = $true 51 | NotifyOnManagerSend = $true 52 | NotifyOnUserSend = $true 53 | NotifyOnUserMatchingRule = $false 54 | NotifyOnUserDaysToExpireNull = $false 55 | SearchPath = "$PSScriptRoot\Search\SearchLog_$((Get-Date).ToString('yyyy-MM')).xml" 56 | EmailDateFormat = "yyyy-MM-dd" 57 | EmailDateFormatUTCConversion = $true 58 | FilterOrganizationalUnit = @( 59 | "*OU=Accounts,OU=Administration,DC=ad,DC=evotec,DC=xyz" 60 | "*OU=Administration,DC=ad,DC=evotec,DC=xyz" 61 | ) 62 | } 63 | New-PasswordConfigurationOption @Options 64 | ``` 65 | 66 | ## 1.2.7 - 2024.08.21 67 | - Adds sending email without credentials (using SMTP server) 68 | - Fixes `SkipCertificateValidation` typo in `New-PasswordConfigurationEmail` 69 | - Small cleanup 70 | 71 | ## 1.2.6 - 2024.08.12 72 | - Fix wrong publish of cmdlets 73 | 74 | ## 1.2.5 - 2024.07.16 75 | - Remove duplicate records from external managers 76 | 77 | ## 1.2.4 - 2024.07.15 78 | - Fixes email address being wrong when using external system and using overwrite property 79 | 80 | ## 1.2.3 - 2024.06.23 81 | - Small report improvements 82 | 83 | ## 1.2.2 - 2024.06.23 84 | - Add reporting for replacements of emails from external sources 85 | 86 | ## 1.2.1 - 2024.06.23 87 | - Allow using DSL and normal configuration at the same time 88 | 89 | ## 1.2.0 - 2024.06.23 90 | - This version adds ability to allow overwritting email address from external system, or even CSV records based on prepared data 91 | - Added `New-PasswordConfigurationExternalUsers` to allow for overwritting emails with external data in form of array of objects 92 | 93 | ## 1.1.1 - 2024.01.16 94 | - Small improvement to error message being provided when sending email fails with summary of emails 95 | 96 | ## 1.1.0 - 2023.11.12 97 | - Add support for weak password hashes (NTLM) in Password Quality Check 98 | - Add new parameters for `Find-PasswordQuality` 99 | - Add new parameters for `Show-PasswordQuality` 100 | 101 | ## 1.0.5 - 2023.10.18 102 | - I can't type apparently so I fixed typo in code 103 | 104 | ## 1.0.3 - 2023.06.12 105 | - Resolves issue with scanning **Active Directory** without exchange attributes 106 | 107 | ## 1.0.2 - 2023.06.06 108 | - Improves `New-PasswordConfigurationRuleReminder` by allowing: `New-PasswordConfigurationRuleReminder -Type 'Manager' -ExpirationDays @(-200..-1), 0, 1, 2, 3, 7, 15, 30, 60 -ComparisonType eq` configuration 109 | 110 | ## 1.0.1 - 2023.06.01 111 | - Fixes AdminSection not working properly 112 | 113 | ## 1.0.0 - 2023.05.25 114 | - Improves exclusions https://github.com/EvotecIT/PasswordSolution/issues/7 115 | - Simplifies configuration https://github.com/EvotecIT/PasswordSolution/issues/8 116 | - Improves configuration https://github.com/EvotecIT/PasswordSolution/issues/3 117 | - Improves configuration https://github.com/EvotecIT/PasswordSolution/issues/6 118 | - Improve HTML report to be better in naming things 119 | - Allow for overwriting manager field with different properties based on SamAccountName/DN 120 | - New configuration option for Report (NestedRules), making separate tab for all rules in HTML 121 | - Small documentation updates 122 | - Adds defaults for templates meaning it's possible to skip their definitions although not very useful if you want personalized thing for your company users 123 | 124 | This actually means: 125 | - We now support DSL language for configuration (see examples) 126 | - We now support much easier way of building configuration 127 | - We now support overwrite email property per rule (global still works) 128 | - We now support overwrite manager property per rule (global still works) 129 | 130 | ## 0.0.37 - 2023.04.25 131 | - Improve `Maps` in Password Quality 132 | - Improve colors, add column to a report 133 | 134 | ## 0.0.36 - 2023.04.24 135 | - Added `Maps` to Password Quality 136 | - Improved reporting in Password Quality with countries for Weak Passwords and Duplicate Groups 137 | - Added Logs to Password Quality 138 | - Minor improvements to reporting 139 | - Make report much smaller 140 | 141 | ## 0.0.35 - 2023.04.19 142 | - Small fix to report 143 | 144 | ## 0.0.34 - 2023.04.19 145 | - Fixes reporting of quality passwords in overview, charts 146 | - Removed tables for Duplicate Groups in favor of single table (1000 tabs don't seem to work in HTML 🤯) 147 | 148 | ## 0.0.33 - 2023.04.18 149 | - General improvements 150 | - Added separate duplicate groups tables to report for easier visibility 151 | - Prefer writable DCs when quering AD 152 | 153 | ## 0.0.32 - 2023.01.18 154 | - Removed DSInternals from required modules (made it optional / disables Password Quality Check). This is to not trigger any security alerts in your environment if you just want to use the module for password expiration and not password quality checks. 155 | 156 | ## 0.0.31 - 2023.01.17 157 | - Small updates to logging 158 | 159 | ## 0.0.30 - 2023.01.17 160 | - Add ability to check password quality (requires higher permissions) and based on DSInternals 161 | 162 | ## 0.0.29 - 2022.10.11 163 | - Fixes to report **ShowSkippedUsers** to skip Contacts 164 | 165 | ## 0.0.28 166 | - Fixes to report **All Users** to skip Contacts 167 | - Fixes to report conditional formatting which would format unnessecary fields 168 | 169 | ## 0.0.26 170 | - Remove Body from **EmailConfiguration** in HTML Report 171 | - Fixes an issue with table in reports having borders (from the EmailBody) 172 | 173 | ## 0.0.25 174 | - Fix ManagerStatus when Overwriteproperty is in use 175 | - Add HTML report to Email to Admins 176 | 177 | ```powershell 178 | HTMLReports = @( 179 | # Accepts a list of reports to generate. Can be multiple reprorts having different sections, or just one having it all 180 | [ordered] @{ 181 | Enable = $true 182 | ShowHTML = $true 183 | Title = "Password Solution Summary" 184 | Online = $true 185 | DisableWarnings = $true 186 | ShowConfiguration = $true 187 | ShowAllUsers = $true 188 | ShowRules = $true 189 | ShowUsersSent = $true 190 | ShowManagersSent = $true 191 | ShowEscalationSent = $true 192 | ShowSkippedUsers = $true 193 | ShowSkippedLocations = $true 194 | ShowSearchUsers = $true 195 | ShowSearchManagers = $true 196 | ShowSearchEscalations = $true 197 | FilePath = "$PSScriptRoot\Reporting\PasswordSolution_$(($Date).ToString('yyyy-MM-dd_HH_mm_ss')).html" 198 | AttachToEmail = $true # new option 199 | } 200 | ) 201 | ``` 202 | 203 | ## 0.0.24 204 | - Bump dependencies of PSWriteHTML, Mailozaurr to newest versions 205 | - Add support for managers of users being a Contact (with an email field) 206 | 207 | ## 0.0.23 208 | - Small cleanup 209 | - Docs update 210 | ## 0.0.22 211 | - Fixes dashboard not showing some objects 212 | - Added properties describing which rule was used to find user 213 | - Added some additional logging -------------------------------------------------------------------------------- /Docs/Find-Password.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PasswordSolution-help.xml 3 | Module Name: PasswordSolution 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Find-Password 9 | 10 | ## SYNOPSIS 11 | Scan Active Directory forest for all users and their password expiration date 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Find-Password [[-Forest] ] [[-ExcludeDomains] ] [[-IncludeDomains] ] 17 | [[-ExtendedForestInformation] ] [[-OverwriteEmailProperty] ] 18 | [[-ReturnObjectsType] ] [[-OverwriteManagerProperty] ] [] 19 | ``` 20 | 21 | ## DESCRIPTION 22 | Scan Active Directory forest for all users and their password expiration date 23 | 24 | ## EXAMPLES 25 | 26 | ### EXAMPLE 1 27 | ``` 28 | Find-Password | ft 29 | ``` 30 | 31 | ## PARAMETERS 32 | 33 | ### -Forest 34 | Target different Forest, by default current forest is used 35 | 36 | ```yaml 37 | Type: String 38 | Parameter Sets: (All) 39 | Aliases: ForestName 40 | 41 | Required: False 42 | Position: 1 43 | Default value: None 44 | Accept pipeline input: False 45 | Accept wildcard characters: False 46 | ``` 47 | 48 | ### -ExcludeDomains 49 | Exclude domain from search, by default whole forest is scanned 50 | 51 | ```yaml 52 | Type: String[] 53 | Parameter Sets: (All) 54 | Aliases: 55 | 56 | Required: False 57 | Position: 2 58 | Default value: None 59 | Accept pipeline input: False 60 | Accept wildcard characters: False 61 | ``` 62 | 63 | ### -IncludeDomains 64 | Include only specific domains, by default whole forest is scanned 65 | 66 | ```yaml 67 | Type: String[] 68 | Parameter Sets: (All) 69 | Aliases: Domain, Domains 70 | 71 | Required: False 72 | Position: 3 73 | Default value: None 74 | Accept pipeline input: False 75 | Accept wildcard characters: False 76 | ``` 77 | 78 | ### -ExtendedForestInformation 79 | Ability to provide Forest Information from another command to speed up processing 80 | 81 | ```yaml 82 | Type: IDictionary 83 | Parameter Sets: (All) 84 | Aliases: 85 | 86 | Required: False 87 | Position: 4 88 | Default value: None 89 | Accept pipeline input: False 90 | Accept wildcard characters: False 91 | ``` 92 | 93 | ### -OverwriteEmailProperty 94 | Overwrite EmailAddress property with different property name 95 | 96 | ```yaml 97 | Type: String 98 | Parameter Sets: (All) 99 | Aliases: 100 | 101 | Required: False 102 | Position: 5 103 | Default value: None 104 | Accept pipeline input: False 105 | Accept wildcard characters: False 106 | ``` 107 | 108 | ### -ReturnObjectsType 109 | {{ Fill ReturnObjectsType Description }} 110 | 111 | ```yaml 112 | Type: String[] 113 | Parameter Sets: (All) 114 | Aliases: 115 | 116 | Required: False 117 | Position: 7 118 | Default value: @('Users', 'Contacts') 119 | Accept pipeline input: False 120 | Accept wildcard characters: False 121 | ``` 122 | 123 | ### -OverwriteManagerProperty 124 | Overwrite Manager property with different property name. 125 | Can use DistinguishedName or SamAccountName 126 | 127 | ```yaml 128 | Type: String 129 | Parameter Sets: (All) 130 | Aliases: 131 | 132 | Required: False 133 | Position: 10 134 | Default value: None 135 | Accept pipeline input: False 136 | Accept wildcard characters: False 137 | ``` 138 | 139 | ### CommonParameters 140 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 141 | 142 | ## INPUTS 143 | 144 | ## OUTPUTS 145 | 146 | ## NOTES 147 | General notes 148 | 149 | ## RELATED LINKS 150 | -------------------------------------------------------------------------------- /Docs/Find-PasswordNotification.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PasswordSolution-help.xml 3 | Module Name: PasswordSolution 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Find-PasswordNotification 9 | 10 | ## SYNOPSIS 11 | Searches thru XML logs created by Password Solution 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Find-PasswordNotification [-SearchPath] [-Manager] [] 17 | ``` 18 | 19 | ## DESCRIPTION 20 | Searches thru XML logs created by Password Solution 21 | 22 | ## EXAMPLES 23 | 24 | ### EXAMPLE 1 25 | ``` 26 | Find-PasswordNotification -SearchPath $PSScriptRoot\Search\SearchLog.xml | Format-Table 27 | ``` 28 | 29 | ### EXAMPLE 2 30 | ``` 31 | Find-PasswordNotification -SearchPath "$PSScriptRoot\Search\SearchLog_2021-06.xml" -Manager | Format-Table 32 | ``` 33 | 34 | ## PARAMETERS 35 | 36 | ### -SearchPath 37 | Path to file where the XML log is located 38 | 39 | ```yaml 40 | Type: String 41 | Parameter Sets: (All) 42 | Aliases: 43 | 44 | Required: True 45 | Position: 1 46 | Default value: None 47 | Accept pipeline input: False 48 | Accept wildcard characters: False 49 | ``` 50 | 51 | ### -Manager 52 | Search thru manager escalations 53 | 54 | ```yaml 55 | Type: SwitchParameter 56 | Parameter Sets: (All) 57 | Aliases: 58 | 59 | Required: False 60 | Position: Named 61 | Default value: False 62 | Accept pipeline input: False 63 | Accept wildcard characters: False 64 | ``` 65 | 66 | ### CommonParameters 67 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 68 | 69 | ## INPUTS 70 | 71 | ## OUTPUTS 72 | 73 | ## NOTES 74 | General notes 75 | 76 | ## RELATED LINKS 77 | -------------------------------------------------------------------------------- /Docs/Find-PasswordQuality.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PasswordSolution-help.xml 3 | Module Name: PasswordSolution 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Find-PasswordQuality 9 | 10 | ## SYNOPSIS 11 | {{ Fill in the Synopsis }} 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Find-PasswordQuality [[-WeakPasswords] ] [-IncludeStatistics] [[-Forest] ] 17 | [[-ExcludeDomains] ] [[-IncludeDomains] ] [[-ExtendedForestInformation] ] 18 | [] 19 | ``` 20 | 21 | ## DESCRIPTION 22 | {{ Fill in the Description }} 23 | 24 | ## EXAMPLES 25 | 26 | ### Example 1 27 | ```powershell 28 | PS C:\> {{ Add example code here }} 29 | ``` 30 | 31 | {{ Add example description here }} 32 | 33 | ## PARAMETERS 34 | 35 | ### -WeakPasswords 36 | {{ Fill WeakPasswords Description }} 37 | 38 | ```yaml 39 | Type: String[] 40 | Parameter Sets: (All) 41 | Aliases: 42 | 43 | Required: False 44 | Position: 1 45 | Default value: None 46 | Accept pipeline input: False 47 | Accept wildcard characters: False 48 | ``` 49 | 50 | ### -IncludeStatistics 51 | {{ Fill IncludeStatistics Description }} 52 | 53 | ```yaml 54 | Type: SwitchParameter 55 | Parameter Sets: (All) 56 | Aliases: 57 | 58 | Required: False 59 | Position: Named 60 | Default value: None 61 | Accept pipeline input: False 62 | Accept wildcard characters: False 63 | ``` 64 | 65 | ### -Forest 66 | Target different Forest, by default current forest is used 67 | 68 | ```yaml 69 | Type: String 70 | Parameter Sets: (All) 71 | Aliases: ForestName 72 | 73 | Required: False 74 | Position: 2 75 | Default value: None 76 | Accept pipeline input: False 77 | Accept wildcard characters: False 78 | ``` 79 | 80 | ### -ExcludeDomains 81 | Exclude domain from search, by default whole forest is scanned 82 | 83 | ```yaml 84 | Type: String[] 85 | Parameter Sets: (All) 86 | Aliases: 87 | 88 | Required: False 89 | Position: 3 90 | Default value: None 91 | Accept pipeline input: False 92 | Accept wildcard characters: False 93 | ``` 94 | 95 | ### -IncludeDomains 96 | Include only specific domains, by default whole forest is scanned 97 | 98 | ```yaml 99 | Type: String[] 100 | Parameter Sets: (All) 101 | Aliases: Domain, Domains 102 | 103 | Required: False 104 | Position: 4 105 | Default value: None 106 | Accept pipeline input: False 107 | Accept wildcard characters: False 108 | ``` 109 | 110 | ### -ExtendedForestInformation 111 | Ability to provide Forest Information from another command to speed up processing 112 | 113 | ```yaml 114 | Type: IDictionary 115 | Parameter Sets: (All) 116 | Aliases: 117 | 118 | Required: False 119 | Position: 5 120 | Default value: None 121 | Accept pipeline input: False 122 | Accept wildcard characters: False 123 | ``` 124 | 125 | ### CommonParameters 126 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 127 | 128 | ## INPUTS 129 | 130 | ### None 131 | 132 | ## OUTPUTS 133 | 134 | ### System.Object 135 | ## NOTES 136 | 137 | ## RELATED LINKS 138 | -------------------------------------------------------------------------------- /Docs/Images/PasswordQuality1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EvotecIT/PasswordSolution/21f10e53d2d545e71ba0d4d83b17e37c756fe972/Docs/Images/PasswordQuality1.png -------------------------------------------------------------------------------- /Docs/Images/PasswordQuality2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EvotecIT/PasswordSolution/21f10e53d2d545e71ba0d4d83b17e37c756fe972/Docs/Images/PasswordQuality2.png -------------------------------------------------------------------------------- /Docs/Images/PasswordQuality3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EvotecIT/PasswordSolution/21f10e53d2d545e71ba0d4d83b17e37c756fe972/Docs/Images/PasswordQuality3.png -------------------------------------------------------------------------------- /Docs/Images/PasswordQuality4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EvotecIT/PasswordSolution/21f10e53d2d545e71ba0d4d83b17e37c756fe972/Docs/Images/PasswordQuality4.png -------------------------------------------------------------------------------- /Docs/Images/PasswordQuality5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EvotecIT/PasswordSolution/21f10e53d2d545e71ba0d4d83b17e37c756fe972/Docs/Images/PasswordQuality5.png -------------------------------------------------------------------------------- /Docs/Images/PasswordQuality6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EvotecIT/PasswordSolution/21f10e53d2d545e71ba0d4d83b17e37c756fe972/Docs/Images/PasswordQuality6.png -------------------------------------------------------------------------------- /Docs/New-PasswordConfigurationEmail.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PasswordSolution-help.xml 3 | Module Name: PasswordSolution 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # New-PasswordConfigurationEmail 9 | 10 | ## SYNOPSIS 11 | {{ Fill in the Synopsis }} 12 | 13 | ## SYNTAX 14 | 15 | ### Compatibility 16 | ``` 17 | New-PasswordConfigurationEmail [-Server ] [-Port ] -From [-ReplyTo ] 18 | [-Priority ] [-DeliveryNotificationOption ] 19 | [-DeliveryStatusNotificationType ] [-Credential ] 20 | [-SecureSocketOptions ] [-UseSsl] [-SkipCertificateRevocation] 21 | [-SkipCertificateValidatation] [-Timeout ] [-LocalDomain ] [-WhatIf] [-Confirm] 22 | [] 23 | ``` 24 | 25 | ### oAuth 26 | ``` 27 | New-PasswordConfigurationEmail [-Server ] [-Port ] -From [-ReplyTo ] 28 | [-Priority ] [-DeliveryNotificationOption ] 29 | [-DeliveryStatusNotificationType ] [-Credential ] 30 | [-SecureSocketOptions ] [-UseSsl] [-SkipCertificateRevocation] 31 | [-SkipCertificateValidatation] [-Timeout ] [-oAuth2] [-LocalDomain ] [-WhatIf] [-Confirm] 32 | [] 33 | ``` 34 | 35 | ### SecureString 36 | ``` 37 | New-PasswordConfigurationEmail [-Server ] [-Port ] -From [-ReplyTo ] 38 | [-Priority ] [-DeliveryNotificationOption ] 39 | [-DeliveryStatusNotificationType ] [-Username ] [-Password ] 40 | [-SecureSocketOptions ] [-UseSsl] [-SkipCertificateRevocation] 41 | [-SkipCertificateValidatation] [-Timeout ] [-AsSecureString] [-LocalDomain ] [-WhatIf] 42 | [-Confirm] [] 43 | ``` 44 | 45 | ### SendGrid 46 | ``` 47 | New-PasswordConfigurationEmail -From [-ReplyTo ] [-Priority ] 48 | -Credential [-SendGrid] [-SeparateTo] [-WhatIf] [-Confirm] [] 49 | ``` 50 | 51 | ### MgGraphRequest 52 | ``` 53 | New-PasswordConfigurationEmail -From [-ReplyTo ] [-Priority ] [-RequestReadReceipt] 54 | [-RequestDeliveryReceipt] [-Graph] [-MgGraphRequest] [-DoNotSaveToSentItems] [-WhatIf] [-Confirm] 55 | [] 56 | ``` 57 | 58 | ### Graph 59 | ``` 60 | New-PasswordConfigurationEmail -From [-ReplyTo ] [-Priority ] 61 | -Credential [-RequestReadReceipt] [-RequestDeliveryReceipt] [-Graph] [-DoNotSaveToSentItems] 62 | [-WhatIf] [-Confirm] [] 63 | ``` 64 | 65 | ## DESCRIPTION 66 | {{ Fill in the Description }} 67 | 68 | ## EXAMPLES 69 | 70 | ### Example 1 71 | ```powershell 72 | PS C:\> {{ Add example code here }} 73 | ``` 74 | 75 | {{ Add example description here }} 76 | 77 | ## PARAMETERS 78 | 79 | ### -AsSecureString 80 | {{ Fill AsSecureString Description }} 81 | 82 | ```yaml 83 | Type: SwitchParameter 84 | Parameter Sets: SecureString 85 | Aliases: 86 | 87 | Required: False 88 | Position: Named 89 | Default value: None 90 | Accept pipeline input: False 91 | Accept wildcard characters: False 92 | ``` 93 | 94 | ### -Confirm 95 | Prompts you for confirmation before running the cmdlet. 96 | 97 | ```yaml 98 | Type: SwitchParameter 99 | Parameter Sets: (All) 100 | Aliases: cf 101 | 102 | Required: False 103 | Position: Named 104 | Default value: None 105 | Accept pipeline input: False 106 | Accept wildcard characters: False 107 | ``` 108 | 109 | ### -Credential 110 | {{ Fill Credential Description }} 111 | 112 | ```yaml 113 | Type: PSCredential 114 | Parameter Sets: Compatibility, oAuth 115 | Aliases: 116 | 117 | Required: False 118 | Position: Named 119 | Default value: None 120 | Accept pipeline input: False 121 | Accept wildcard characters: False 122 | ``` 123 | 124 | ```yaml 125 | Type: PSCredential 126 | Parameter Sets: SendGrid, Graph 127 | Aliases: 128 | 129 | Required: True 130 | Position: Named 131 | Default value: None 132 | Accept pipeline input: False 133 | Accept wildcard characters: False 134 | ``` 135 | 136 | ### -DeliveryNotificationOption 137 | {{ Fill DeliveryNotificationOption Description }} 138 | 139 | ```yaml 140 | Type: String[] 141 | Parameter Sets: Compatibility, oAuth, SecureString 142 | Aliases: 143 | Accepted values: None, OnSuccess, OnFailure, Delay, Never 144 | 145 | Required: False 146 | Position: Named 147 | Default value: None 148 | Accept pipeline input: False 149 | Accept wildcard characters: False 150 | ``` 151 | 152 | ### -DeliveryStatusNotificationType 153 | {{ Fill DeliveryStatusNotificationType Description }} 154 | 155 | ```yaml 156 | Type: DeliveryStatusNotificationType 157 | Parameter Sets: Compatibility, oAuth, SecureString 158 | Aliases: 159 | Accepted values: Unspecified, Full, HeadersOnly 160 | 161 | Required: False 162 | Position: Named 163 | Default value: None 164 | Accept pipeline input: False 165 | Accept wildcard characters: False 166 | ``` 167 | 168 | ### -DoNotSaveToSentItems 169 | {{ Fill DoNotSaveToSentItems Description }} 170 | 171 | ```yaml 172 | Type: SwitchParameter 173 | Parameter Sets: MgGraphRequest, Graph 174 | Aliases: 175 | 176 | Required: False 177 | Position: Named 178 | Default value: None 179 | Accept pipeline input: False 180 | Accept wildcard characters: False 181 | ``` 182 | 183 | ### -From 184 | {{ Fill From Description }} 185 | 186 | ```yaml 187 | Type: Object 188 | Parameter Sets: (All) 189 | Aliases: 190 | 191 | Required: True 192 | Position: Named 193 | Default value: None 194 | Accept pipeline input: False 195 | Accept wildcard characters: False 196 | ``` 197 | 198 | ### -Graph 199 | {{ Fill Graph Description }} 200 | 201 | ```yaml 202 | Type: SwitchParameter 203 | Parameter Sets: MgGraphRequest, Graph 204 | Aliases: 205 | 206 | Required: False 207 | Position: Named 208 | Default value: None 209 | Accept pipeline input: False 210 | Accept wildcard characters: False 211 | ``` 212 | 213 | ### -LocalDomain 214 | {{ Fill LocalDomain Description }} 215 | 216 | ```yaml 217 | Type: String 218 | Parameter Sets: Compatibility, oAuth, SecureString 219 | Aliases: 220 | 221 | Required: False 222 | Position: Named 223 | Default value: None 224 | Accept pipeline input: False 225 | Accept wildcard characters: False 226 | ``` 227 | 228 | ### -MgGraphRequest 229 | {{ Fill MgGraphRequest Description }} 230 | 231 | ```yaml 232 | Type: SwitchParameter 233 | Parameter Sets: MgGraphRequest 234 | Aliases: 235 | 236 | Required: False 237 | Position: Named 238 | Default value: None 239 | Accept pipeline input: False 240 | Accept wildcard characters: False 241 | ``` 242 | 243 | ### -Password 244 | {{ Fill Password Description }} 245 | 246 | ```yaml 247 | Type: String 248 | Parameter Sets: SecureString 249 | Aliases: 250 | 251 | Required: False 252 | Position: Named 253 | Default value: None 254 | Accept pipeline input: False 255 | Accept wildcard characters: False 256 | ``` 257 | 258 | ### -Port 259 | {{ Fill Port Description }} 260 | 261 | ```yaml 262 | Type: Int32 263 | Parameter Sets: Compatibility, oAuth, SecureString 264 | Aliases: 265 | 266 | Required: False 267 | Position: Named 268 | Default value: None 269 | Accept pipeline input: False 270 | Accept wildcard characters: False 271 | ``` 272 | 273 | ### -Priority 274 | {{ Fill Priority Description }} 275 | 276 | ```yaml 277 | Type: String 278 | Parameter Sets: (All) 279 | Aliases: Importance 280 | Accepted values: Low, Normal, High 281 | 282 | Required: False 283 | Position: Named 284 | Default value: None 285 | Accept pipeline input: False 286 | Accept wildcard characters: False 287 | ``` 288 | 289 | ### -ReplyTo 290 | {{ Fill ReplyTo Description }} 291 | 292 | ```yaml 293 | Type: String 294 | Parameter Sets: (All) 295 | Aliases: 296 | 297 | Required: False 298 | Position: Named 299 | Default value: None 300 | Accept pipeline input: False 301 | Accept wildcard characters: False 302 | ``` 303 | 304 | ### -RequestDeliveryReceipt 305 | {{ Fill RequestDeliveryReceipt Description }} 306 | 307 | ```yaml 308 | Type: SwitchParameter 309 | Parameter Sets: MgGraphRequest, Graph 310 | Aliases: 311 | 312 | Required: False 313 | Position: Named 314 | Default value: None 315 | Accept pipeline input: False 316 | Accept wildcard characters: False 317 | ``` 318 | 319 | ### -RequestReadReceipt 320 | {{ Fill RequestReadReceipt Description }} 321 | 322 | ```yaml 323 | Type: SwitchParameter 324 | Parameter Sets: MgGraphRequest, Graph 325 | Aliases: 326 | 327 | Required: False 328 | Position: Named 329 | Default value: None 330 | Accept pipeline input: False 331 | Accept wildcard characters: False 332 | ``` 333 | 334 | ### -SecureSocketOptions 335 | {{ Fill SecureSocketOptions Description }} 336 | 337 | ```yaml 338 | Type: SecureSocketOptions 339 | Parameter Sets: Compatibility, oAuth, SecureString 340 | Aliases: 341 | Accepted values: None, Auto, SslOnConnect, StartTls, StartTlsWhenAvailable 342 | 343 | Required: False 344 | Position: Named 345 | Default value: None 346 | Accept pipeline input: False 347 | Accept wildcard characters: False 348 | ``` 349 | 350 | ### -SendGrid 351 | {{ Fill SendGrid Description }} 352 | 353 | ```yaml 354 | Type: SwitchParameter 355 | Parameter Sets: SendGrid 356 | Aliases: 357 | 358 | Required: False 359 | Position: Named 360 | Default value: None 361 | Accept pipeline input: False 362 | Accept wildcard characters: False 363 | ``` 364 | 365 | ### -SeparateTo 366 | {{ Fill SeparateTo Description }} 367 | 368 | ```yaml 369 | Type: SwitchParameter 370 | Parameter Sets: SendGrid 371 | Aliases: 372 | 373 | Required: False 374 | Position: Named 375 | Default value: None 376 | Accept pipeline input: False 377 | Accept wildcard characters: False 378 | ``` 379 | 380 | ### -Server 381 | {{ Fill Server Description }} 382 | 383 | ```yaml 384 | Type: String 385 | Parameter Sets: Compatibility, oAuth, SecureString 386 | Aliases: SmtpServer 387 | 388 | Required: False 389 | Position: Named 390 | Default value: None 391 | Accept pipeline input: False 392 | Accept wildcard characters: False 393 | ``` 394 | 395 | ### -SkipCertificateRevocation 396 | {{ Fill SkipCertificateRevocation Description }} 397 | 398 | ```yaml 399 | Type: SwitchParameter 400 | Parameter Sets: Compatibility, oAuth, SecureString 401 | Aliases: 402 | 403 | Required: False 404 | Position: Named 405 | Default value: None 406 | Accept pipeline input: False 407 | Accept wildcard characters: False 408 | ``` 409 | 410 | ### -SkipCertificateValidatation 411 | {{ Fill SkipCertificateValidatation Description }} 412 | 413 | ```yaml 414 | Type: SwitchParameter 415 | Parameter Sets: Compatibility, oAuth, SecureString 416 | Aliases: 417 | 418 | Required: False 419 | Position: Named 420 | Default value: None 421 | Accept pipeline input: False 422 | Accept wildcard characters: False 423 | ``` 424 | 425 | ### -Timeout 426 | {{ Fill Timeout Description }} 427 | 428 | ```yaml 429 | Type: Int32 430 | Parameter Sets: Compatibility, oAuth, SecureString 431 | Aliases: 432 | 433 | Required: False 434 | Position: Named 435 | Default value: None 436 | Accept pipeline input: False 437 | Accept wildcard characters: False 438 | ``` 439 | 440 | ### -UseSsl 441 | {{ Fill UseSsl Description }} 442 | 443 | ```yaml 444 | Type: SwitchParameter 445 | Parameter Sets: Compatibility, oAuth, SecureString 446 | Aliases: 447 | 448 | Required: False 449 | Position: Named 450 | Default value: None 451 | Accept pipeline input: False 452 | Accept wildcard characters: False 453 | ``` 454 | 455 | ### -Username 456 | {{ Fill Username Description }} 457 | 458 | ```yaml 459 | Type: String 460 | Parameter Sets: SecureString 461 | Aliases: 462 | 463 | Required: False 464 | Position: Named 465 | Default value: None 466 | Accept pipeline input: False 467 | Accept wildcard characters: False 468 | ``` 469 | 470 | ### -WhatIf 471 | Shows what would happen if the cmdlet runs. The cmdlet is not run. 472 | 473 | ```yaml 474 | Type: SwitchParameter 475 | Parameter Sets: (All) 476 | Aliases: wi 477 | 478 | Required: False 479 | Position: Named 480 | Default value: None 481 | Accept pipeline input: False 482 | Accept wildcard characters: False 483 | ``` 484 | 485 | ### -oAuth2 486 | {{ Fill oAuth2 Description }} 487 | 488 | ```yaml 489 | Type: SwitchParameter 490 | Parameter Sets: oAuth 491 | Aliases: oAuth 492 | 493 | Required: False 494 | Position: Named 495 | Default value: None 496 | Accept pipeline input: False 497 | Accept wildcard characters: False 498 | ``` 499 | 500 | ### CommonParameters 501 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 502 | 503 | ## INPUTS 504 | 505 | ### None 506 | 507 | ## OUTPUTS 508 | 509 | ### System.Object 510 | ## NOTES 511 | 512 | ## RELATED LINKS 513 | -------------------------------------------------------------------------------- /Docs/New-PasswordConfigurationOption.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PasswordSolution-help.xml 3 | Module Name: PasswordSolution 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # New-PasswordConfigurationOption 9 | 10 | ## SYNOPSIS 11 | {{ Fill in the Synopsis }} 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | New-PasswordConfigurationOption [-ShowTime] [[-LogFile] ] [[-TimeFormat] ] 17 | [[-LogMaximum] ] [-NotifyOnSkipUserManagerOnly] [-NotifyOnSecuritySend] [-NotifyOnManagerSend] 18 | [-NotifyOnUserSend] [-NotifyOnUserMatchingRule] [-NotifyOnUserDaysToExpireNull] [[-SearchPath] ] 19 | [[-EmailDateFormat] ] [-EmailDateFormatUTCConversion] [[-OverwriteEmailProperty] ] 20 | [[-OverwriteManagerProperty] ] [] 21 | ``` 22 | 23 | ## DESCRIPTION 24 | {{ Fill in the Description }} 25 | 26 | ## EXAMPLES 27 | 28 | ### Example 1 29 | ```powershell 30 | PS C:\> {{ Add example code here }} 31 | ``` 32 | 33 | {{ Add example description here }} 34 | 35 | ## PARAMETERS 36 | 37 | ### -EmailDateFormat 38 | {{ Fill EmailDateFormat Description }} 39 | 40 | ```yaml 41 | Type: String 42 | Parameter Sets: (All) 43 | Aliases: 44 | 45 | Required: False 46 | Position: 4 47 | Default value: None 48 | Accept pipeline input: False 49 | Accept wildcard characters: False 50 | ``` 51 | 52 | ### -EmailDateFormatUTCConversion 53 | {{ Fill EmailDateFormatUTCConversion Description }} 54 | 55 | ```yaml 56 | Type: SwitchParameter 57 | Parameter Sets: (All) 58 | Aliases: 59 | 60 | Required: False 61 | Position: Named 62 | Default value: None 63 | Accept pipeline input: False 64 | Accept wildcard characters: False 65 | ``` 66 | 67 | ### -LogFile 68 | {{ Fill LogFile Description }} 69 | 70 | ```yaml 71 | Type: String 72 | Parameter Sets: (All) 73 | Aliases: 74 | 75 | Required: False 76 | Position: 0 77 | Default value: None 78 | Accept pipeline input: False 79 | Accept wildcard characters: False 80 | ``` 81 | 82 | ### -LogMaximum 83 | {{ Fill LogMaximum Description }} 84 | 85 | ```yaml 86 | Type: Int32 87 | Parameter Sets: (All) 88 | Aliases: 89 | 90 | Required: False 91 | Position: 2 92 | Default value: None 93 | Accept pipeline input: False 94 | Accept wildcard characters: False 95 | ``` 96 | 97 | ### -NotifyOnManagerSend 98 | {{ Fill NotifyOnManagerSend Description }} 99 | 100 | ```yaml 101 | Type: SwitchParameter 102 | Parameter Sets: (All) 103 | Aliases: 104 | 105 | Required: False 106 | Position: Named 107 | Default value: None 108 | Accept pipeline input: False 109 | Accept wildcard characters: False 110 | ``` 111 | 112 | ### -NotifyOnSecuritySend 113 | {{ Fill NotifyOnSecuritySend Description }} 114 | 115 | ```yaml 116 | Type: SwitchParameter 117 | Parameter Sets: (All) 118 | Aliases: 119 | 120 | Required: False 121 | Position: Named 122 | Default value: None 123 | Accept pipeline input: False 124 | Accept wildcard characters: False 125 | ``` 126 | 127 | ### -NotifyOnSkipUserManagerOnly 128 | {{ Fill NotifyOnSkipUserManagerOnly Description }} 129 | 130 | ```yaml 131 | Type: SwitchParameter 132 | Parameter Sets: (All) 133 | Aliases: 134 | 135 | Required: False 136 | Position: Named 137 | Default value: None 138 | Accept pipeline input: False 139 | Accept wildcard characters: False 140 | ``` 141 | 142 | ### -NotifyOnUserDaysToExpireNull 143 | {{ Fill NotifyOnUserDaysToExpireNull Description }} 144 | 145 | ```yaml 146 | Type: SwitchParameter 147 | Parameter Sets: (All) 148 | Aliases: 149 | 150 | Required: False 151 | Position: Named 152 | Default value: None 153 | Accept pipeline input: False 154 | Accept wildcard characters: False 155 | ``` 156 | 157 | ### -NotifyOnUserMatchingRule 158 | {{ Fill NotifyOnUserMatchingRule Description }} 159 | 160 | ```yaml 161 | Type: SwitchParameter 162 | Parameter Sets: (All) 163 | Aliases: 164 | 165 | Required: False 166 | Position: Named 167 | Default value: None 168 | Accept pipeline input: False 169 | Accept wildcard characters: False 170 | ``` 171 | 172 | ### -NotifyOnUserSend 173 | {{ Fill NotifyOnUserSend Description }} 174 | 175 | ```yaml 176 | Type: SwitchParameter 177 | Parameter Sets: (All) 178 | Aliases: 179 | 180 | Required: False 181 | Position: Named 182 | Default value: None 183 | Accept pipeline input: False 184 | Accept wildcard characters: False 185 | ``` 186 | 187 | ### -OverwriteEmailProperty 188 | {{ Fill OverwriteEmailProperty Description }} 189 | 190 | ```yaml 191 | Type: String 192 | Parameter Sets: (All) 193 | Aliases: 194 | 195 | Required: False 196 | Position: 5 197 | Default value: None 198 | Accept pipeline input: False 199 | Accept wildcard characters: False 200 | ``` 201 | 202 | ### -OverwriteManagerProperty 203 | {{ Fill OverwriteManagerProperty Description }} 204 | 205 | ```yaml 206 | Type: String 207 | Parameter Sets: (All) 208 | Aliases: 209 | 210 | Required: False 211 | Position: 6 212 | Default value: None 213 | Accept pipeline input: False 214 | Accept wildcard characters: False 215 | ``` 216 | 217 | ### -SearchPath 218 | {{ Fill SearchPath Description }} 219 | 220 | ```yaml 221 | Type: String 222 | Parameter Sets: (All) 223 | Aliases: 224 | 225 | Required: False 226 | Position: 3 227 | Default value: None 228 | Accept pipeline input: False 229 | Accept wildcard characters: False 230 | ``` 231 | 232 | ### -ShowTime 233 | {{ Fill ShowTime Description }} 234 | 235 | ```yaml 236 | Type: SwitchParameter 237 | Parameter Sets: (All) 238 | Aliases: 239 | 240 | Required: False 241 | Position: Named 242 | Default value: None 243 | Accept pipeline input: False 244 | Accept wildcard characters: False 245 | ``` 246 | 247 | ### -TimeFormat 248 | {{ Fill TimeFormat Description }} 249 | 250 | ```yaml 251 | Type: String 252 | Parameter Sets: (All) 253 | Aliases: 254 | 255 | Required: False 256 | Position: 1 257 | Default value: None 258 | Accept pipeline input: False 259 | Accept wildcard characters: False 260 | ``` 261 | 262 | ### CommonParameters 263 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 264 | 265 | ## INPUTS 266 | 267 | ### None 268 | 269 | ## OUTPUTS 270 | 271 | ### System.Object 272 | ## NOTES 273 | 274 | ## RELATED LINKS 275 | -------------------------------------------------------------------------------- /Docs/New-PasswordConfigurationReport.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PasswordSolution-help.xml 3 | Module Name: PasswordSolution 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # New-PasswordConfigurationReport 9 | 10 | ## SYNOPSIS 11 | {{ Fill in the Synopsis }} 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | New-PasswordConfigurationReport [-Enable] [-ShowHTML] [[-Title] ] [-Online] [-DisableWarnings] 17 | [-ShowConfiguration] [-ShowAllUsers] [-ShowRules] [-ShowUsersSent] [-ShowManagersSent] [-ShowEscalationSent] 18 | [-ShowSkippedUsers] [-ShowSkippedLocations] [-ShowSearchUsers] [-ShowSearchManagers] [-ShowSearchEscalations] 19 | [[-FilePath] ] [-AttachToEmail] [-NestedRules] [] 20 | ``` 21 | 22 | ## DESCRIPTION 23 | {{ Fill in the Description }} 24 | 25 | ## EXAMPLES 26 | 27 | ### Example 1 28 | ```powershell 29 | PS C:\> {{ Add example code here }} 30 | ``` 31 | 32 | {{ Add example description here }} 33 | 34 | ## PARAMETERS 35 | 36 | ### -Enable 37 | {{ Fill Enable Description }} 38 | 39 | ```yaml 40 | Type: SwitchParameter 41 | Parameter Sets: (All) 42 | Aliases: 43 | 44 | Required: False 45 | Position: Named 46 | Default value: None 47 | Accept pipeline input: False 48 | Accept wildcard characters: False 49 | ``` 50 | 51 | ### -ShowHTML 52 | {{ Fill ShowHTML Description }} 53 | 54 | ```yaml 55 | Type: SwitchParameter 56 | Parameter Sets: (All) 57 | Aliases: 58 | 59 | Required: False 60 | Position: Named 61 | Default value: None 62 | Accept pipeline input: False 63 | Accept wildcard characters: False 64 | ``` 65 | 66 | ### -Title 67 | {{ Fill Title Description }} 68 | 69 | ```yaml 70 | Type: String 71 | Parameter Sets: (All) 72 | Aliases: 73 | 74 | Required: False 75 | Position: 1 76 | Default value: None 77 | Accept pipeline input: False 78 | Accept wildcard characters: False 79 | ``` 80 | 81 | ### -Online 82 | {{ Fill Online Description }} 83 | 84 | ```yaml 85 | Type: SwitchParameter 86 | Parameter Sets: (All) 87 | Aliases: 88 | 89 | Required: False 90 | Position: Named 91 | Default value: None 92 | Accept pipeline input: False 93 | Accept wildcard characters: False 94 | ``` 95 | 96 | ### -DisableWarnings 97 | {{ Fill DisableWarnings Description }} 98 | 99 | ```yaml 100 | Type: SwitchParameter 101 | Parameter Sets: (All) 102 | Aliases: 103 | 104 | Required: False 105 | Position: Named 106 | Default value: None 107 | Accept pipeline input: False 108 | Accept wildcard characters: False 109 | ``` 110 | 111 | ### -ShowConfiguration 112 | {{ Fill ShowConfiguration Description }} 113 | 114 | ```yaml 115 | Type: SwitchParameter 116 | Parameter Sets: (All) 117 | Aliases: 118 | 119 | Required: False 120 | Position: Named 121 | Default value: None 122 | Accept pipeline input: False 123 | Accept wildcard characters: False 124 | ``` 125 | 126 | ### -ShowAllUsers 127 | {{ Fill ShowAllUsers Description }} 128 | 129 | ```yaml 130 | Type: SwitchParameter 131 | Parameter Sets: (All) 132 | Aliases: 133 | 134 | Required: False 135 | Position: Named 136 | Default value: None 137 | Accept pipeline input: False 138 | Accept wildcard characters: False 139 | ``` 140 | 141 | ### -ShowRules 142 | {{ Fill ShowRules Description }} 143 | 144 | ```yaml 145 | Type: SwitchParameter 146 | Parameter Sets: (All) 147 | Aliases: 148 | 149 | Required: False 150 | Position: Named 151 | Default value: None 152 | Accept pipeline input: False 153 | Accept wildcard characters: False 154 | ``` 155 | 156 | ### -ShowUsersSent 157 | {{ Fill ShowUsersSent Description }} 158 | 159 | ```yaml 160 | Type: SwitchParameter 161 | Parameter Sets: (All) 162 | Aliases: 163 | 164 | Required: False 165 | Position: Named 166 | Default value: None 167 | Accept pipeline input: False 168 | Accept wildcard characters: False 169 | ``` 170 | 171 | ### -ShowManagersSent 172 | {{ Fill ShowManagersSent Description }} 173 | 174 | ```yaml 175 | Type: SwitchParameter 176 | Parameter Sets: (All) 177 | Aliases: 178 | 179 | Required: False 180 | Position: Named 181 | Default value: None 182 | Accept pipeline input: False 183 | Accept wildcard characters: False 184 | ``` 185 | 186 | ### -ShowEscalationSent 187 | {{ Fill ShowEscalationSent Description }} 188 | 189 | ```yaml 190 | Type: SwitchParameter 191 | Parameter Sets: (All) 192 | Aliases: 193 | 194 | Required: False 195 | Position: Named 196 | Default value: None 197 | Accept pipeline input: False 198 | Accept wildcard characters: False 199 | ``` 200 | 201 | ### -ShowSkippedUsers 202 | {{ Fill ShowSkippedUsers Description }} 203 | 204 | ```yaml 205 | Type: SwitchParameter 206 | Parameter Sets: (All) 207 | Aliases: 208 | 209 | Required: False 210 | Position: Named 211 | Default value: None 212 | Accept pipeline input: False 213 | Accept wildcard characters: False 214 | ``` 215 | 216 | ### -ShowSkippedLocations 217 | {{ Fill ShowSkippedLocations Description }} 218 | 219 | ```yaml 220 | Type: SwitchParameter 221 | Parameter Sets: (All) 222 | Aliases: 223 | 224 | Required: False 225 | Position: Named 226 | Default value: None 227 | Accept pipeline input: False 228 | Accept wildcard characters: False 229 | ``` 230 | 231 | ### -ShowSearchUsers 232 | {{ Fill ShowSearchUsers Description }} 233 | 234 | ```yaml 235 | Type: SwitchParameter 236 | Parameter Sets: (All) 237 | Aliases: 238 | 239 | Required: False 240 | Position: Named 241 | Default value: None 242 | Accept pipeline input: False 243 | Accept wildcard characters: False 244 | ``` 245 | 246 | ### -ShowSearchManagers 247 | {{ Fill ShowSearchManagers Description }} 248 | 249 | ```yaml 250 | Type: SwitchParameter 251 | Parameter Sets: (All) 252 | Aliases: 253 | 254 | Required: False 255 | Position: Named 256 | Default value: None 257 | Accept pipeline input: False 258 | Accept wildcard characters: False 259 | ``` 260 | 261 | ### -ShowSearchEscalations 262 | {{ Fill ShowSearchEscalations Description }} 263 | 264 | ```yaml 265 | Type: SwitchParameter 266 | Parameter Sets: (All) 267 | Aliases: 268 | 269 | Required: False 270 | Position: Named 271 | Default value: None 272 | Accept pipeline input: False 273 | Accept wildcard characters: False 274 | ``` 275 | 276 | ### -FilePath 277 | {{ Fill FilePath Description }} 278 | 279 | ```yaml 280 | Type: String 281 | Parameter Sets: (All) 282 | Aliases: 283 | 284 | Required: False 285 | Position: 2 286 | Default value: None 287 | Accept pipeline input: False 288 | Accept wildcard characters: False 289 | ``` 290 | 291 | ### -AttachToEmail 292 | {{ Fill AttachToEmail Description }} 293 | 294 | ```yaml 295 | Type: SwitchParameter 296 | Parameter Sets: (All) 297 | Aliases: 298 | 299 | Required: False 300 | Position: Named 301 | Default value: None 302 | Accept pipeline input: False 303 | Accept wildcard characters: False 304 | ``` 305 | 306 | ### -NestedRules 307 | Specifies whether to display nested password rules. 308 | Each rule has it's own tab with output. 309 | Having many rules and all other settings enabled can result in a very long list of tabs that's hard to navigate. 310 | This setting forces separate tab for all rules. 311 | The default value is $false. 312 | 313 | ```yaml 314 | Type: SwitchParameter 315 | Parameter Sets: (All) 316 | Aliases: 317 | 318 | Required: False 319 | Position: Named 320 | Default value: False 321 | Accept pipeline input: False 322 | Accept wildcard characters: False 323 | ``` 324 | 325 | ### CommonParameters 326 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 327 | 328 | ## INPUTS 329 | 330 | ### None 331 | 332 | ## OUTPUTS 333 | 334 | ### System.Object 335 | ## NOTES 336 | 337 | ## RELATED LINKS 338 | -------------------------------------------------------------------------------- /Docs/New-PasswordConfigurationRule.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PasswordSolution-help.xml 3 | Module Name: PasswordSolution 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # New-PasswordConfigurationRule 9 | 10 | ## SYNOPSIS 11 | {{ Fill in the Synopsis }} 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | New-PasswordConfigurationRule [[-ReminderConfiguration] ] [-Name] [-Enable] 17 | [-IncludeExpiring] [-IncludePasswordNeverExpires] [[-PasswordNeverExpiresDays] ] 18 | [[-IncludeNameProperties] ] [[-IncludeName] ] [[-ExcludeNameProperties] ] 19 | [[-ExcludeName] ] [[-IncludeOU] ] [[-ExcludeOU] ] [[-IncludeGroup] ] 20 | [[-ExcludeGroup] ] [-ReminderDays] [-ManagerReminder] [-ManagerNotCompliant] 21 | [[-ManagerNotCompliantDisplayName] ] [[-ManagerNotCompliantEmailAddress] ] 22 | [-ManagerNotCompliantDisabled] [-ManagerNotCompliantMissing] [-ManagerNotCompliantMissingEmail] 23 | [[-ManagerNotCompliantLastLogonDays] ] [-SecurityEscalation] 24 | [[-SecurityEscalationDisplayName] ] [[-SecurityEscalationEmailAddress] ] 25 | [[-OverwriteEmailProperty] ] [[-OverwriteManagerProperty] ] [-ProcessManagersOnly] 26 | [] 27 | ``` 28 | 29 | ## DESCRIPTION 30 | {{ Fill in the Description }} 31 | 32 | ## EXAMPLES 33 | 34 | ### Example 1 35 | ```powershell 36 | PS C:\> {{ Add example code here }} 37 | ``` 38 | 39 | {{ Add example description here }} 40 | 41 | ## PARAMETERS 42 | 43 | ### -ReminderConfiguration 44 | {{ Fill ReminderConfiguration Description }} 45 | 46 | ```yaml 47 | Type: ScriptBlock 48 | Parameter Sets: (All) 49 | Aliases: 50 | 51 | Required: False 52 | Position: 1 53 | Default value: None 54 | Accept pipeline input: False 55 | Accept wildcard characters: False 56 | ``` 57 | 58 | ### -Name 59 | {{ Fill Name Description }} 60 | 61 | ```yaml 62 | Type: String 63 | Parameter Sets: (All) 64 | Aliases: 65 | 66 | Required: True 67 | Position: 2 68 | Default value: None 69 | Accept pipeline input: False 70 | Accept wildcard characters: False 71 | ``` 72 | 73 | ### -Enable 74 | {{ Fill Enable Description }} 75 | 76 | ```yaml 77 | Type: SwitchParameter 78 | Parameter Sets: (All) 79 | Aliases: 80 | 81 | Required: False 82 | Position: Named 83 | Default value: None 84 | Accept pipeline input: False 85 | Accept wildcard characters: False 86 | ``` 87 | 88 | ### -IncludeExpiring 89 | {{ Fill IncludeExpiring Description }} 90 | 91 | ```yaml 92 | Type: SwitchParameter 93 | Parameter Sets: (All) 94 | Aliases: 95 | 96 | Required: False 97 | Position: Named 98 | Default value: None 99 | Accept pipeline input: False 100 | Accept wildcard characters: False 101 | ``` 102 | 103 | ### -IncludePasswordNeverExpires 104 | {{ Fill IncludePasswordNeverExpires Description }} 105 | 106 | ```yaml 107 | Type: SwitchParameter 108 | Parameter Sets: (All) 109 | Aliases: 110 | 111 | Required: False 112 | Position: Named 113 | Default value: None 114 | Accept pipeline input: False 115 | Accept wildcard characters: False 116 | ``` 117 | 118 | ### -PasswordNeverExpiresDays 119 | {{ Fill PasswordNeverExpiresDays Description }} 120 | 121 | ```yaml 122 | Type: Int32 123 | Parameter Sets: (All) 124 | Aliases: 125 | 126 | Required: False 127 | Position: 3 128 | Default value: None 129 | Accept pipeline input: False 130 | Accept wildcard characters: False 131 | ``` 132 | 133 | ### -IncludeNameProperties 134 | {{ Fill IncludeNameProperties Description }} 135 | 136 | ```yaml 137 | Type: String[] 138 | Parameter Sets: (All) 139 | Aliases: 140 | 141 | Required: False 142 | Position: 4 143 | Default value: None 144 | Accept pipeline input: False 145 | Accept wildcard characters: False 146 | ``` 147 | 148 | ### -IncludeName 149 | {{ Fill IncludeName Description }} 150 | 151 | ```yaml 152 | Type: String[] 153 | Parameter Sets: (All) 154 | Aliases: 155 | 156 | Required: False 157 | Position: 5 158 | Default value: None 159 | Accept pipeline input: False 160 | Accept wildcard characters: False 161 | ``` 162 | 163 | ### -ExcludeNameProperties 164 | Exclude user from rule if any of the properties match the value as defined in ExcludeName 165 | 166 | ```yaml 167 | Type: String[] 168 | Parameter Sets: (All) 169 | Aliases: 170 | 171 | Required: False 172 | Position: 6 173 | Default value: None 174 | Accept pipeline input: False 175 | Accept wildcard characters: False 176 | ``` 177 | 178 | ### -ExcludeName 179 | Exclude user from rule if any of the properties match the value of Name in the properties defined in ExcludeNameProperties 180 | 181 | ```yaml 182 | Type: String[] 183 | Parameter Sets: (All) 184 | Aliases: 185 | 186 | Required: False 187 | Position: 7 188 | Default value: None 189 | Accept pipeline input: False 190 | Accept wildcard characters: False 191 | ``` 192 | 193 | ### -IncludeOU 194 | {{ Fill IncludeOU Description }} 195 | 196 | ```yaml 197 | Type: String[] 198 | Parameter Sets: (All) 199 | Aliases: 200 | 201 | Required: False 202 | Position: 8 203 | Default value: None 204 | Accept pipeline input: False 205 | Accept wildcard characters: False 206 | ``` 207 | 208 | ### -ExcludeOU 209 | {{ Fill ExcludeOU Description }} 210 | 211 | ```yaml 212 | Type: String[] 213 | Parameter Sets: (All) 214 | Aliases: 215 | 216 | Required: False 217 | Position: 9 218 | Default value: None 219 | Accept pipeline input: False 220 | Accept wildcard characters: False 221 | ``` 222 | 223 | ### -IncludeGroup 224 | Parameter description 225 | 226 | ```yaml 227 | Type: String[] 228 | Parameter Sets: (All) 229 | Aliases: 230 | 231 | Required: False 232 | Position: 10 233 | Default value: None 234 | Accept pipeline input: False 235 | Accept wildcard characters: False 236 | ``` 237 | 238 | ### -ExcludeGroup 239 | Parameter description 240 | 241 | ```yaml 242 | Type: String[] 243 | Parameter Sets: (All) 244 | Aliases: 245 | 246 | Required: False 247 | Position: 11 248 | Default value: None 249 | Accept pipeline input: False 250 | Accept wildcard characters: False 251 | ``` 252 | 253 | ### -ReminderDays 254 | Parameter description 255 | 256 | ```yaml 257 | Type: Array 258 | Parameter Sets: (All) 259 | Aliases: ExpirationDays, Days 260 | 261 | Required: True 262 | Position: 12 263 | Default value: None 264 | Accept pipeline input: False 265 | Accept wildcard characters: False 266 | ``` 267 | 268 | ### -ManagerReminder 269 | {{ Fill ManagerReminder Description }} 270 | 271 | ```yaml 272 | Type: SwitchParameter 273 | Parameter Sets: (All) 274 | Aliases: 275 | 276 | Required: False 277 | Position: Named 278 | Default value: None 279 | Accept pipeline input: False 280 | Accept wildcard characters: False 281 | ``` 282 | 283 | ### -ManagerNotCompliant 284 | {{ Fill ManagerNotCompliant Description }} 285 | 286 | ```yaml 287 | Type: SwitchParameter 288 | Parameter Sets: (All) 289 | Aliases: 290 | 291 | Required: False 292 | Position: Named 293 | Default value: None 294 | Accept pipeline input: False 295 | Accept wildcard characters: False 296 | ``` 297 | 298 | ### -ManagerNotCompliantDisplayName 299 | {{ Fill ManagerNotCompliantDisplayName Description }} 300 | 301 | ```yaml 302 | Type: String 303 | Parameter Sets: (All) 304 | Aliases: 305 | 306 | Required: False 307 | Position: 13 308 | Default value: None 309 | Accept pipeline input: False 310 | Accept wildcard characters: False 311 | ``` 312 | 313 | ### -ManagerNotCompliantEmailAddress 314 | {{ Fill ManagerNotCompliantEmailAddress Description }} 315 | 316 | ```yaml 317 | Type: String 318 | Parameter Sets: (All) 319 | Aliases: 320 | 321 | Required: False 322 | Position: 14 323 | Default value: None 324 | Accept pipeline input: False 325 | Accept wildcard characters: False 326 | ``` 327 | 328 | ### -ManagerNotCompliantDisabled 329 | {{ Fill ManagerNotCompliantDisabled Description }} 330 | 331 | ```yaml 332 | Type: SwitchParameter 333 | Parameter Sets: (All) 334 | Aliases: 335 | 336 | Required: False 337 | Position: Named 338 | Default value: None 339 | Accept pipeline input: False 340 | Accept wildcard characters: False 341 | ``` 342 | 343 | ### -ManagerNotCompliantMissing 344 | {{ Fill ManagerNotCompliantMissing Description }} 345 | 346 | ```yaml 347 | Type: SwitchParameter 348 | Parameter Sets: (All) 349 | Aliases: 350 | 351 | Required: False 352 | Position: Named 353 | Default value: None 354 | Accept pipeline input: False 355 | Accept wildcard characters: False 356 | ``` 357 | 358 | ### -ManagerNotCompliantMissingEmail 359 | {{ Fill ManagerNotCompliantMissingEmail Description }} 360 | 361 | ```yaml 362 | Type: SwitchParameter 363 | Parameter Sets: (All) 364 | Aliases: 365 | 366 | Required: False 367 | Position: Named 368 | Default value: None 369 | Accept pipeline input: False 370 | Accept wildcard characters: False 371 | ``` 372 | 373 | ### -ManagerNotCompliantLastLogonDays 374 | {{ Fill ManagerNotCompliantLastLogonDays Description }} 375 | 376 | ```yaml 377 | Type: Int32 378 | Parameter Sets: (All) 379 | Aliases: 380 | 381 | Required: False 382 | Position: 15 383 | Default value: None 384 | Accept pipeline input: False 385 | Accept wildcard characters: False 386 | ``` 387 | 388 | ### -SecurityEscalation 389 | {{ Fill SecurityEscalation Description }} 390 | 391 | ```yaml 392 | Type: SwitchParameter 393 | Parameter Sets: (All) 394 | Aliases: 395 | 396 | Required: False 397 | Position: Named 398 | Default value: None 399 | Accept pipeline input: False 400 | Accept wildcard characters: False 401 | ``` 402 | 403 | ### -SecurityEscalationDisplayName 404 | {{ Fill SecurityEscalationDisplayName Description }} 405 | 406 | ```yaml 407 | Type: String 408 | Parameter Sets: (All) 409 | Aliases: 410 | 411 | Required: False 412 | Position: 16 413 | Default value: None 414 | Accept pipeline input: False 415 | Accept wildcard characters: False 416 | ``` 417 | 418 | ### -SecurityEscalationEmailAddress 419 | {{ Fill SecurityEscalationEmailAddress Description }} 420 | 421 | ```yaml 422 | Type: String 423 | Parameter Sets: (All) 424 | Aliases: 425 | 426 | Required: False 427 | Position: 17 428 | Default value: None 429 | Accept pipeline input: False 430 | Accept wildcard characters: False 431 | ``` 432 | 433 | ### -OverwriteEmailProperty 434 | Parameter description 435 | 436 | ```yaml 437 | Type: String 438 | Parameter Sets: (All) 439 | Aliases: 440 | 441 | Required: False 442 | Position: 18 443 | Default value: None 444 | Accept pipeline input: False 445 | Accept wildcard characters: False 446 | ``` 447 | 448 | ### -OverwriteManagerProperty 449 | Parameter description 450 | 451 | ```yaml 452 | Type: String 453 | Parameter Sets: (All) 454 | Aliases: 455 | 456 | Required: False 457 | Position: 19 458 | Default value: None 459 | Accept pipeline input: False 460 | Accept wildcard characters: False 461 | ``` 462 | 463 | ### -ProcessManagersOnly 464 | This parameters is used to process users, but only managers will be notified. 465 | Sending emails to users within the rule will be skipped completly. 466 | This is useful if users would have email addresses, that would normally trigger an email to them. 467 | 468 | ```yaml 469 | Type: SwitchParameter 470 | Parameter Sets: (All) 471 | Aliases: 472 | 473 | Required: False 474 | Position: Named 475 | Default value: False 476 | Accept pipeline input: False 477 | Accept wildcard characters: False 478 | ``` 479 | 480 | ### CommonParameters 481 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 482 | 483 | ## INPUTS 484 | 485 | ### None 486 | 487 | ## OUTPUTS 488 | 489 | ### System.Object 490 | ## NOTES 491 | 492 | ## RELATED LINKS 493 | -------------------------------------------------------------------------------- /Docs/New-PasswordConfigurationRuleReminder.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PasswordSolution-help.xml 3 | Module Name: PasswordSolution 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # New-PasswordConfigurationRuleReminder 9 | 10 | ## SYNOPSIS 11 | {{ Fill in the Synopsis }} 12 | 13 | ## SYNTAX 14 | 15 | ### Daily (Default) 16 | ``` 17 | New-PasswordConfigurationRuleReminder -Type -ExpirationDays [-ComparisonType ] 18 | [] 19 | ``` 20 | 21 | ### DayOfMonth 22 | ``` 23 | New-PasswordConfigurationRuleReminder -Type -ExpirationDays -DayOfMonth 24 | [-ComparisonType ] [] 25 | ``` 26 | 27 | ### DayOfWeek 28 | ``` 29 | New-PasswordConfigurationRuleReminder -Type -ExpirationDays -DayOfWeek 30 | [-ComparisonType ] [] 31 | ``` 32 | 33 | ## DESCRIPTION 34 | {{ Fill in the Description }} 35 | 36 | ## EXAMPLES 37 | 38 | ### Example 1 39 | ```powershell 40 | PS C:\> {{ Add example code here }} 41 | ``` 42 | 43 | {{ Add example description here }} 44 | 45 | ## PARAMETERS 46 | 47 | ### -ComparisonType 48 | {{ Fill ComparisonType Description }} 49 | 50 | ```yaml 51 | Type: String 52 | Parameter Sets: (All) 53 | Aliases: 54 | Accepted values: lt, gt, eq, in 55 | 56 | Required: False 57 | Position: Named 58 | Default value: None 59 | Accept pipeline input: False 60 | Accept wildcard characters: False 61 | ``` 62 | 63 | ### -DayOfMonth 64 | {{ Fill DayOfMonth Description }} 65 | 66 | ```yaml 67 | Type: Array 68 | Parameter Sets: DayOfMonth 69 | Aliases: 70 | 71 | Required: True 72 | Position: Named 73 | Default value: None 74 | Accept pipeline input: False 75 | Accept wildcard characters: False 76 | ``` 77 | 78 | ### -DayOfWeek 79 | {{ Fill DayOfWeek Description }} 80 | 81 | ```yaml 82 | Type: Array 83 | Parameter Sets: DayOfWeek 84 | Aliases: 85 | Accepted values: Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday 86 | 87 | Required: True 88 | Position: Named 89 | Default value: None 90 | Accept pipeline input: False 91 | Accept wildcard characters: False 92 | ``` 93 | 94 | ### -ExpirationDays 95 | {{ Fill ExpirationDays Description }} 96 | 97 | ```yaml 98 | Type: Array 99 | Parameter Sets: (All) 100 | Aliases: ConditionDays, Days 101 | 102 | Required: True 103 | Position: Named 104 | Default value: None 105 | Accept pipeline input: False 106 | Accept wildcard characters: False 107 | ``` 108 | 109 | ### -Type 110 | {{ Fill Type Description }} 111 | 112 | ```yaml 113 | Type: String 114 | Parameter Sets: (All) 115 | Aliases: 116 | Accepted values: Manager, ManagerNotCompliant, Security 117 | 118 | Required: True 119 | Position: Named 120 | Default value: None 121 | Accept pipeline input: False 122 | Accept wildcard characters: False 123 | ``` 124 | 125 | ### CommonParameters 126 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 127 | 128 | ## INPUTS 129 | 130 | ### None 131 | 132 | ## OUTPUTS 133 | 134 | ### System.Object 135 | ## NOTES 136 | 137 | ## RELATED LINKS 138 | -------------------------------------------------------------------------------- /Docs/New-PasswordConfigurationTemplate.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PasswordSolution-help.xml 3 | Module Name: PasswordSolution 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # New-PasswordConfigurationTemplate 9 | 10 | ## SYNOPSIS 11 | {{ Fill in the Synopsis }} 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | New-PasswordConfigurationTemplate [-Template] [-Subject] [-Type] 17 | [] 18 | ``` 19 | 20 | ## DESCRIPTION 21 | {{ Fill in the Description }} 22 | 23 | ## EXAMPLES 24 | 25 | ### Example 1 26 | ```powershell 27 | PS C:\> {{ Add example code here }} 28 | ``` 29 | 30 | {{ Add example description here }} 31 | 32 | ## PARAMETERS 33 | 34 | ### -Subject 35 | {{ Fill Subject Description }} 36 | 37 | ```yaml 38 | Type: String 39 | Parameter Sets: (All) 40 | Aliases: 41 | 42 | Required: True 43 | Position: 1 44 | Default value: None 45 | Accept pipeline input: False 46 | Accept wildcard characters: False 47 | ``` 48 | 49 | ### -Template 50 | {{ Fill Template Description }} 51 | 52 | ```yaml 53 | Type: ScriptBlock 54 | Parameter Sets: (All) 55 | Aliases: 56 | 57 | Required: True 58 | Position: 0 59 | Default value: None 60 | Accept pipeline input: False 61 | Accept wildcard characters: False 62 | ``` 63 | 64 | ### -Type 65 | {{ Fill Type Description }} 66 | 67 | ```yaml 68 | Type: Object 69 | Parameter Sets: (All) 70 | Aliases: 71 | Accepted values: PreExpiry, PostExpiry, Manager, ManagerNotCompliant, Security, Admin 72 | 73 | Required: True 74 | Position: 2 75 | Default value: None 76 | Accept pipeline input: False 77 | Accept wildcard characters: False 78 | ``` 79 | 80 | ### CommonParameters 81 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 82 | 83 | ## INPUTS 84 | 85 | ### None 86 | 87 | ## OUTPUTS 88 | 89 | ### System.Object 90 | ## NOTES 91 | 92 | ## RELATED LINKS 93 | -------------------------------------------------------------------------------- /Docs/New-PasswordConfigurationType.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PasswordSolution-help.xml 3 | Module Name: PasswordSolution 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # New-PasswordConfigurationType 9 | 10 | ## SYNOPSIS 11 | {{ Fill in the Synopsis }} 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | New-PasswordConfigurationType [-Type] [-Enable] [[-SendCountMaximum] ] 17 | [[-DefaultEmail] ] [-AttachCSV] [] 18 | ``` 19 | 20 | ## DESCRIPTION 21 | {{ Fill in the Description }} 22 | 23 | ## EXAMPLES 24 | 25 | ### Example 1 26 | ```powershell 27 | PS C:\> {{ Add example code here }} 28 | ``` 29 | 30 | {{ Add example description here }} 31 | 32 | ## PARAMETERS 33 | 34 | ### -AttachCSV 35 | {{ Fill AttachCSV Description }} 36 | 37 | ```yaml 38 | Type: SwitchParameter 39 | Parameter Sets: (All) 40 | Aliases: 41 | 42 | Required: False 43 | Position: Named 44 | Default value: None 45 | Accept pipeline input: False 46 | Accept wildcard characters: False 47 | ``` 48 | 49 | ### -DefaultEmail 50 | {{ Fill DefaultEmail Description }} 51 | 52 | ```yaml 53 | Type: String 54 | Parameter Sets: (All) 55 | Aliases: 56 | 57 | Required: False 58 | Position: 2 59 | Default value: None 60 | Accept pipeline input: False 61 | Accept wildcard characters: False 62 | ``` 63 | 64 | ### -Enable 65 | {{ Fill Enable Description }} 66 | 67 | ```yaml 68 | Type: SwitchParameter 69 | Parameter Sets: (All) 70 | Aliases: 71 | 72 | Required: False 73 | Position: Named 74 | Default value: None 75 | Accept pipeline input: False 76 | Accept wildcard characters: False 77 | ``` 78 | 79 | ### -SendCountMaximum 80 | {{ Fill SendCountMaximum Description }} 81 | 82 | ```yaml 83 | Type: Int32 84 | Parameter Sets: (All) 85 | Aliases: 86 | 87 | Required: False 88 | Position: 1 89 | Default value: None 90 | Accept pipeline input: False 91 | Accept wildcard characters: False 92 | ``` 93 | 94 | ### -Type 95 | {{ Fill Type Description }} 96 | 97 | ```yaml 98 | Type: String 99 | Parameter Sets: (All) 100 | Aliases: 101 | Accepted values: User, Manager, Security, Admin 102 | 103 | Required: True 104 | Position: 0 105 | Default value: None 106 | Accept pipeline input: False 107 | Accept wildcard characters: False 108 | ``` 109 | 110 | ### CommonParameters 111 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 112 | 113 | ## INPUTS 114 | 115 | ### None 116 | 117 | ## OUTPUTS 118 | 119 | ### System.Object 120 | ## NOTES 121 | 122 | ## RELATED LINKS 123 | -------------------------------------------------------------------------------- /Docs/Readme.md: -------------------------------------------------------------------------------- 1 | --- 2 | Module Name: PasswordSolution 3 | Module Guid: c58ff818-1de6-4500-961c-a243c2043255 4 | Download Help Link: {{ Update Download Link }} 5 | Help Version: {{ Please enter version of help manually (X.X.X.X) format }} 6 | Locale: en-US 7 | --- 8 | 9 | # PasswordSolution Module 10 | ## Description 11 | PasswordSolution is a PowerShell module that provides Password Expiry notifications to users, managers, security and administrators. It's very configurable and was designed for enterprise use. 12 | 13 | ## PasswordSolution Cmdlets 14 | ### [Find-Password](Find-Password.md) 15 | Scan Active Directory forest for all users and their password expiration date 16 | 17 | ### [Find-PasswordNotification](Find-PasswordNotification.md) 18 | Searches thru XML logs created by Password Solution 19 | 20 | ### [Find-PasswordQuality](Find-PasswordQuality.md) 21 | {{ Fill in the Synopsis }} 22 | 23 | ### [New-PasswordConfigurationEmail](New-PasswordConfigurationEmail.md) 24 | {{ Fill in the Synopsis }} 25 | 26 | ### [New-PasswordConfigurationOption](New-PasswordConfigurationOption.md) 27 | {{ Fill in the Synopsis }} 28 | 29 | ### [New-PasswordConfigurationReport](New-PasswordConfigurationReport.md) 30 | {{ Fill in the Synopsis }} 31 | 32 | ### [New-PasswordConfigurationRule](New-PasswordConfigurationRule.md) 33 | {{ Fill in the Synopsis }} 34 | 35 | ### [New-PasswordConfigurationRuleReminder](New-PasswordConfigurationRuleReminder.md) 36 | {{ Fill in the Synopsis }} 37 | 38 | ### [New-PasswordConfigurationTemplate](New-PasswordConfigurationTemplate.md) 39 | {{ Fill in the Synopsis }} 40 | 41 | ### [New-PasswordConfigurationType](New-PasswordConfigurationType.md) 42 | {{ Fill in the Synopsis }} 43 | 44 | ### [Show-PasswordQuality](Show-PasswordQuality.md) 45 | {{ Fill in the Synopsis }} 46 | 47 | ### [Start-PasswordSolution](Start-PasswordSolution.md) 48 | Starts Password Expiry Notifications for the whole forest 49 | 50 | -------------------------------------------------------------------------------- /Docs/Show-PasswordQuality.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PasswordSolution-help.xml 3 | Module Name: PasswordSolution 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Show-PasswordQuality 9 | 10 | ## SYNOPSIS 11 | {{ Fill in the Synopsis }} 12 | 13 | ## SYNTAX 14 | 15 | ``` 16 | Show-PasswordQuality [[-Forest] ] [[-ExcludeDomains] ] [[-IncludeDomains] ] 17 | [[-ExtendedForestInformation] ] [[-FilePath] ] [-DontShow] [-Online] 18 | [[-WeakPasswords] ] [-SeparateDuplicateGroups] [-PassThru] [-AddWorldMap] [[-LogPath] ] 19 | [[-LogMaximum] ] [-LogShowTime] [[-LogTimeFormat] ] [] 20 | ``` 21 | 22 | ## DESCRIPTION 23 | {{ Fill in the Description }} 24 | 25 | ## EXAMPLES 26 | 27 | ### Example 1 28 | ```powershell 29 | PS C:\> {{ Add example code here }} 30 | ``` 31 | 32 | {{ Add example description here }} 33 | 34 | ## PARAMETERS 35 | 36 | ### -Forest 37 | {{ Fill Forest Description }} 38 | 39 | ```yaml 40 | Type: String 41 | Parameter Sets: (All) 42 | Aliases: ForestName 43 | 44 | Required: False 45 | Position: 1 46 | Default value: None 47 | Accept pipeline input: False 48 | Accept wildcard characters: False 49 | ``` 50 | 51 | ### -ExcludeDomains 52 | {{ Fill ExcludeDomains Description }} 53 | 54 | ```yaml 55 | Type: String[] 56 | Parameter Sets: (All) 57 | Aliases: 58 | 59 | Required: False 60 | Position: 2 61 | Default value: None 62 | Accept pipeline input: False 63 | Accept wildcard characters: False 64 | ``` 65 | 66 | ### -IncludeDomains 67 | {{ Fill IncludeDomains Description }} 68 | 69 | ```yaml 70 | Type: String[] 71 | Parameter Sets: (All) 72 | Aliases: Domain, Domains 73 | 74 | Required: False 75 | Position: 3 76 | Default value: None 77 | Accept pipeline input: False 78 | Accept wildcard characters: False 79 | ``` 80 | 81 | ### -ExtendedForestInformation 82 | {{ Fill ExtendedForestInformation Description }} 83 | 84 | ```yaml 85 | Type: IDictionary 86 | Parameter Sets: (All) 87 | Aliases: 88 | 89 | Required: False 90 | Position: 4 91 | Default value: None 92 | Accept pipeline input: False 93 | Accept wildcard characters: False 94 | ``` 95 | 96 | ### -FilePath 97 | {{ Fill FilePath Description }} 98 | 99 | ```yaml 100 | Type: String 101 | Parameter Sets: (All) 102 | Aliases: 103 | 104 | Required: False 105 | Position: 5 106 | Default value: None 107 | Accept pipeline input: False 108 | Accept wildcard characters: False 109 | ``` 110 | 111 | ### -DontShow 112 | {{ Fill DontShow Description }} 113 | 114 | ```yaml 115 | Type: SwitchParameter 116 | Parameter Sets: (All) 117 | Aliases: 118 | 119 | Required: False 120 | Position: Named 121 | Default value: None 122 | Accept pipeline input: False 123 | Accept wildcard characters: False 124 | ``` 125 | 126 | ### -Online 127 | {{ Fill Online Description }} 128 | 129 | ```yaml 130 | Type: SwitchParameter 131 | Parameter Sets: (All) 132 | Aliases: 133 | 134 | Required: False 135 | Position: Named 136 | Default value: None 137 | Accept pipeline input: False 138 | Accept wildcard characters: False 139 | ``` 140 | 141 | ### -WeakPasswords 142 | {{ Fill WeakPasswords Description }} 143 | 144 | ```yaml 145 | Type: String[] 146 | Parameter Sets: (All) 147 | Aliases: 148 | 149 | Required: False 150 | Position: 6 151 | Default value: None 152 | Accept pipeline input: False 153 | Accept wildcard characters: False 154 | ``` 155 | 156 | ### -SeparateDuplicateGroups 157 | If specified, report will show duplicate groups separately, one group per tab. 158 | 159 | ```yaml 160 | Type: SwitchParameter 161 | Parameter Sets: (All) 162 | Aliases: 163 | 164 | Required: False 165 | Position: Named 166 | Default value: False 167 | Accept pipeline input: False 168 | Accept wildcard characters: False 169 | ``` 170 | 171 | ### -PassThru 172 | {{ Fill PassThru Description }} 173 | 174 | ```yaml 175 | Type: SwitchParameter 176 | Parameter Sets: (All) 177 | Aliases: 178 | 179 | Required: False 180 | Position: Named 181 | Default value: False 182 | Accept pipeline input: False 183 | Accept wildcard characters: False 184 | ``` 185 | 186 | ### -AddWorldMap 187 | {{ Fill AddWorldMap Description }} 188 | 189 | ```yaml 190 | Type: SwitchParameter 191 | Parameter Sets: (All) 192 | Aliases: 193 | 194 | Required: False 195 | Position: Named 196 | Default value: False 197 | Accept pipeline input: False 198 | Accept wildcard characters: False 199 | ``` 200 | 201 | ### -LogPath 202 | {{ Fill LogPath Description }} 203 | 204 | ```yaml 205 | Type: String 206 | Parameter Sets: (All) 207 | Aliases: LogFile 208 | 209 | Required: False 210 | Position: 7 211 | Default value: None 212 | Accept pipeline input: False 213 | Accept wildcard characters: False 214 | ``` 215 | 216 | ### -LogMaximum 217 | {{ Fill LogMaximum Description }} 218 | 219 | ```yaml 220 | Type: Int32 221 | Parameter Sets: (All) 222 | Aliases: 223 | 224 | Required: False 225 | Position: 8 226 | Default value: 0 227 | Accept pipeline input: False 228 | Accept wildcard characters: False 229 | ``` 230 | 231 | ### -LogShowTime 232 | {{ Fill LogShowTime Description }} 233 | 234 | ```yaml 235 | Type: SwitchParameter 236 | Parameter Sets: (All) 237 | Aliases: 238 | 239 | Required: False 240 | Position: Named 241 | Default value: False 242 | Accept pipeline input: False 243 | Accept wildcard characters: False 244 | ``` 245 | 246 | ### -LogTimeFormat 247 | {{ Fill LogTimeFormat Description }} 248 | 249 | ```yaml 250 | Type: String 251 | Parameter Sets: (All) 252 | Aliases: 253 | 254 | Required: False 255 | Position: 9 256 | Default value: Yyyy-MM-dd HH:mm:ss 257 | Accept pipeline input: False 258 | Accept wildcard characters: False 259 | ``` 260 | 261 | ### CommonParameters 262 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 263 | 264 | ## INPUTS 265 | 266 | ### None 267 | 268 | ## OUTPUTS 269 | 270 | ### System.Object 271 | ## NOTES 272 | 273 | ## RELATED LINKS 274 | -------------------------------------------------------------------------------- /Docs/Start-PasswordSolution.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PasswordSolution-help.xml 3 | Module Name: PasswordSolution 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Start-PasswordSolution 9 | 10 | ## SYNOPSIS 11 | Starts Password Expiry Notifications for the whole forest 12 | 13 | ## SYNTAX 14 | 15 | ### DSL (Default) 16 | ``` 17 | Start-PasswordSolution [[-ConfigurationDSL] ] [] 18 | ``` 19 | 20 | ### Legacy 21 | ``` 22 | Start-PasswordSolution -EmailParameters [-OverwriteEmailProperty ] 23 | [-OverwriteManagerProperty ] -UserSection -ManagerSection 24 | -SecuritySection -AdminSection -Rules [-TemplatePreExpiry ] 25 | [-TemplatePreExpirySubject ] [-TemplatePostExpiry ] [-TemplatePostExpirySubject ] 26 | -TemplateManager -TemplateManagerSubject -TemplateSecurity 27 | -TemplateSecuritySubject -TemplateManagerNotCompliant 28 | -TemplateManagerNotCompliantSubject -TemplateAdmin -TemplateAdminSubject 29 | [-Logging ] [-HTMLReports ] [-SearchPath ] [] 30 | ``` 31 | 32 | ## DESCRIPTION 33 | Starts Password Expiry Notifications for the whole forest 34 | 35 | ## EXAMPLES 36 | 37 | ### EXAMPLE 1 38 | ``` 39 | An example 40 | ``` 41 | 42 | ## PARAMETERS 43 | 44 | ### -ConfigurationDSL 45 | Parameter description 46 | 47 | ```yaml 48 | Type: ScriptBlock 49 | Parameter Sets: DSL 50 | Aliases: 51 | 52 | Required: False 53 | Position: 1 54 | Default value: None 55 | Accept pipeline input: False 56 | Accept wildcard characters: False 57 | ``` 58 | 59 | ### -EmailParameters 60 | Parameters for Email. 61 | Uses Mailozaurr splatting behind the scenes, so it supports all options that Mailozaurr does. 62 | 63 | ```yaml 64 | Type: IDictionary 65 | Parameter Sets: Legacy 66 | Aliases: 67 | 68 | Required: True 69 | Position: Named 70 | Default value: None 71 | Accept pipeline input: False 72 | Accept wildcard characters: False 73 | ``` 74 | 75 | ### -OverwriteEmailProperty 76 | Property responsible for overwriting the default email field in Active Directory. 77 | Useful when the password notification has to go somewhere else than users email address. 78 | 79 | ```yaml 80 | Type: String 81 | Parameter Sets: Legacy 82 | Aliases: 83 | 84 | Required: False 85 | Position: Named 86 | Default value: None 87 | Accept pipeline input: False 88 | Accept wildcard characters: False 89 | ``` 90 | 91 | ### -OverwriteManagerProperty 92 | {{ Fill OverwriteManagerProperty Description }} 93 | 94 | ```yaml 95 | Type: String 96 | Parameter Sets: Legacy 97 | Aliases: 98 | 99 | Required: False 100 | Position: Named 101 | Default value: None 102 | Accept pipeline input: False 103 | Accept wildcard characters: False 104 | ``` 105 | 106 | ### -UserSection 107 | Parameter description 108 | 109 | ```yaml 110 | Type: IDictionary 111 | Parameter Sets: Legacy 112 | Aliases: 113 | 114 | Required: True 115 | Position: Named 116 | Default value: None 117 | Accept pipeline input: False 118 | Accept wildcard characters: False 119 | ``` 120 | 121 | ### -ManagerSection 122 | Parameter description 123 | 124 | ```yaml 125 | Type: IDictionary 126 | Parameter Sets: Legacy 127 | Aliases: 128 | 129 | Required: True 130 | Position: Named 131 | Default value: None 132 | Accept pipeline input: False 133 | Accept wildcard characters: False 134 | ``` 135 | 136 | ### -SecuritySection 137 | Parameter description 138 | 139 | ```yaml 140 | Type: IDictionary 141 | Parameter Sets: Legacy 142 | Aliases: 143 | 144 | Required: True 145 | Position: Named 146 | Default value: None 147 | Accept pipeline input: False 148 | Accept wildcard characters: False 149 | ``` 150 | 151 | ### -AdminSection 152 | Parameter description 153 | 154 | ```yaml 155 | Type: IDictionary 156 | Parameter Sets: Legacy 157 | Aliases: 158 | 159 | Required: True 160 | Position: Named 161 | Default value: None 162 | Accept pipeline input: False 163 | Accept wildcard characters: False 164 | ``` 165 | 166 | ### -Rules 167 | Parameter description 168 | 169 | ```yaml 170 | Type: Array 171 | Parameter Sets: Legacy 172 | Aliases: 173 | 174 | Required: True 175 | Position: Named 176 | Default value: None 177 | Accept pipeline input: False 178 | Accept wildcard characters: False 179 | ``` 180 | 181 | ### -TemplatePreExpiry 182 | Parameter description 183 | 184 | ```yaml 185 | Type: ScriptBlock 186 | Parameter Sets: Legacy 187 | Aliases: 188 | 189 | Required: False 190 | Position: Named 191 | Default value: None 192 | Accept pipeline input: False 193 | Accept wildcard characters: False 194 | ``` 195 | 196 | ### -TemplatePreExpirySubject 197 | Parameter description 198 | 199 | ```yaml 200 | Type: String 201 | Parameter Sets: Legacy 202 | Aliases: 203 | 204 | Required: False 205 | Position: Named 206 | Default value: None 207 | Accept pipeline input: False 208 | Accept wildcard characters: False 209 | ``` 210 | 211 | ### -TemplatePostExpiry 212 | Parameter description 213 | 214 | ```yaml 215 | Type: ScriptBlock 216 | Parameter Sets: Legacy 217 | Aliases: 218 | 219 | Required: False 220 | Position: Named 221 | Default value: None 222 | Accept pipeline input: False 223 | Accept wildcard characters: False 224 | ``` 225 | 226 | ### -TemplatePostExpirySubject 227 | Parameter description 228 | 229 | ```yaml 230 | Type: String 231 | Parameter Sets: Legacy 232 | Aliases: 233 | 234 | Required: False 235 | Position: Named 236 | Default value: None 237 | Accept pipeline input: False 238 | Accept wildcard characters: False 239 | ``` 240 | 241 | ### -TemplateManager 242 | Parameter description 243 | 244 | ```yaml 245 | Type: ScriptBlock 246 | Parameter Sets: Legacy 247 | Aliases: 248 | 249 | Required: True 250 | Position: Named 251 | Default value: None 252 | Accept pipeline input: False 253 | Accept wildcard characters: False 254 | ``` 255 | 256 | ### -TemplateManagerSubject 257 | Parameter description 258 | 259 | ```yaml 260 | Type: String 261 | Parameter Sets: Legacy 262 | Aliases: 263 | 264 | Required: True 265 | Position: Named 266 | Default value: None 267 | Accept pipeline input: False 268 | Accept wildcard characters: False 269 | ``` 270 | 271 | ### -TemplateSecurity 272 | Parameter description 273 | 274 | ```yaml 275 | Type: ScriptBlock 276 | Parameter Sets: Legacy 277 | Aliases: 278 | 279 | Required: True 280 | Position: Named 281 | Default value: None 282 | Accept pipeline input: False 283 | Accept wildcard characters: False 284 | ``` 285 | 286 | ### -TemplateSecuritySubject 287 | Parameter description 288 | 289 | ```yaml 290 | Type: String 291 | Parameter Sets: Legacy 292 | Aliases: 293 | 294 | Required: True 295 | Position: Named 296 | Default value: None 297 | Accept pipeline input: False 298 | Accept wildcard characters: False 299 | ``` 300 | 301 | ### -TemplateManagerNotCompliant 302 | Parameter description 303 | 304 | ```yaml 305 | Type: ScriptBlock 306 | Parameter Sets: Legacy 307 | Aliases: 308 | 309 | Required: True 310 | Position: Named 311 | Default value: None 312 | Accept pipeline input: False 313 | Accept wildcard characters: False 314 | ``` 315 | 316 | ### -TemplateManagerNotCompliantSubject 317 | Parameter description 318 | 319 | ```yaml 320 | Type: String 321 | Parameter Sets: Legacy 322 | Aliases: 323 | 324 | Required: True 325 | Position: Named 326 | Default value: None 327 | Accept pipeline input: False 328 | Accept wildcard characters: False 329 | ``` 330 | 331 | ### -TemplateAdmin 332 | Parameter description 333 | 334 | ```yaml 335 | Type: ScriptBlock 336 | Parameter Sets: Legacy 337 | Aliases: 338 | 339 | Required: True 340 | Position: Named 341 | Default value: None 342 | Accept pipeline input: False 343 | Accept wildcard characters: False 344 | ``` 345 | 346 | ### -TemplateAdminSubject 347 | Parameter description 348 | 349 | ```yaml 350 | Type: String 351 | Parameter Sets: Legacy 352 | Aliases: 353 | 354 | Required: True 355 | Position: Named 356 | Default value: None 357 | Accept pipeline input: False 358 | Accept wildcard characters: False 359 | ``` 360 | 361 | ### -Logging 362 | Parameter description 363 | 364 | ```yaml 365 | Type: IDictionary 366 | Parameter Sets: Legacy 367 | Aliases: 368 | 369 | Required: False 370 | Position: Named 371 | Default value: None 372 | Accept pipeline input: False 373 | Accept wildcard characters: False 374 | ``` 375 | 376 | ### -HTMLReports 377 | Parameter description 378 | 379 | ```yaml 380 | Type: Array 381 | Parameter Sets: Legacy 382 | Aliases: 383 | 384 | Required: False 385 | Position: Named 386 | Default value: None 387 | Accept pipeline input: False 388 | Accept wildcard characters: False 389 | ``` 390 | 391 | ### -SearchPath 392 | Parameter description 393 | 394 | ```yaml 395 | Type: String 396 | Parameter Sets: Legacy 397 | Aliases: 398 | 399 | Required: False 400 | Position: Named 401 | Default value: None 402 | Accept pipeline input: False 403 | Accept wildcard characters: False 404 | ``` 405 | 406 | ### CommonParameters 407 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 408 | 409 | ## INPUTS 410 | 411 | ## OUTPUTS 412 | 413 | ## NOTES 414 | General notes 415 | 416 | ## RELATED LINKS 417 | -------------------------------------------------------------------------------- /Examples/Example-EncryptPassword.ps1: -------------------------------------------------------------------------------- 1 | (Get-Credential -UserName 'test' -Message 'Password').Password | ConvertFrom-SecureString | Set-Clipboard -------------------------------------------------------------------------------- /Examples/Example-FindUsers.ps1: -------------------------------------------------------------------------------- 1 | Import-Module .\PasswordSolution.psd1 -Force 2 | 3 | $Users = Find-Password -OverwriteEmailProperty 'extensionAttribute7' -OverwriteManagerProperty extensionAttribute1 -FilterOrganizationalUnit @( 4 | "*OU=Accounts,OU=Administration,DC=ad,DC=evotec,DC=xyz" 5 | ) 6 | $Users | Format-Table UserPrincipalName, Name, Domain, Type, SamAccountName, OrganizationalUnit, Manager, ManagerEmail, ManagerStatus 7 | #$Users | Sort-Object -Property Manager | Format-Table Name, Manager, ManagerSamAccountName, ManagerEmail, ManagerStatus, ManagerLastLogonDays, ManagerType, Domain, UserPrincipalName -------------------------------------------------------------------------------- /Examples/Example-FindUsersAdvanced.ps1: -------------------------------------------------------------------------------- 1 | Import-Module .\PasswordSolution.psd1 -Force 2 | 3 | $RuleProperties = @( 4 | 'ExtensionAttribute7' 5 | 'extensionAttribute7' 6 | 'extensionAttribute8' 7 | ) 8 | 9 | $Users = Find-Password -OverwriteEmailProperty 'extensionAttribute7' -RulesProperties $RuleProperties #| Where-Object { $_.Name -eq 'Test Contact' } 10 | #$Users | Format-Table Name, Extension*, DateExpiry #Manager, ManagerSamAccountName, ManagerEmail, ManagerStatus, ManagerLastLogonDays, ManagerType, Domain, UserPrincipalName 11 | 12 | $Users | Select-Object -First 5 | Format-Table Name, Extension*, DateExpiry, LastLogonDate, LastLogonDays -------------------------------------------------------------------------------- /Examples/Example-FindUsersEntra.ps1: -------------------------------------------------------------------------------- 1 | Import-Module .\PasswordSolution.psd1 -Force 2 | 3 | Connect-MgGraph -Scopes "User.Read.All" -NoWelcome 4 | 5 | $Users = Find-PasswordEntra #-Verbose 6 | #$Users | Format-Table 7 | $Users | Out-HtmlView -ScrollX -Filtering -DataStore JavaScript { 8 | New-HTMLTableCondition -Name 'Enabled' -ComparisonType string -Value $True -BackgroundColor TeaGreen -FailBackgroundColor Salmon 9 | New-HTMLTableCondition -Name 'IsLicensed' -ComparisonType string -Value $True -BackgroundColor TeaGreen -FailBackgroundColor Salmon 10 | New-HTMLTableCondition -Name 'IsSynchronized' -ComparisonType string -Value $True -BackgroundColor TeaGreen -FailBackgroundColor Salmon 11 | New-HTMLTableCondition -Name 'PasswordExpired' -ComparisonType string -Value $false -BackgroundColor TeaGreen -FailBackgroundColor Salmon 12 | } -DisablePaging -------------------------------------------------------------------------------- /Examples/Example-FindUsersPasswordQualityConsole.ps1: -------------------------------------------------------------------------------- 1 | Import-Module .\PasswordSolution.psd1 -Force 2 | 3 | $Users = Find-PasswordQuality 4 | $Users | Format-Table 5 | 6 | #$Users = Find-PasswordQuality -IncludeDomains 'ad.evotec.pl' 7 | #$Users | Format-Table -------------------------------------------------------------------------------- /Examples/Example-FindUsersPasswordQualityConsoleWithReplacements.ps1: -------------------------------------------------------------------------------- 1 | Import-Module .\PasswordSolution.psd1 -Force 2 | 3 | $Replacements = @( 4 | New-PasswordConfigurationReplacement -PropertyName 'Country' -Type eq -PropertyReplacementHash @{ 5 | 'PL' = 'Poland' 6 | 'DE' = 'Germany' 7 | 'AT' = 'Austria' 8 | 'IT' = 'Italy' 9 | 'Unknown' = 'Not specified in AD' 10 | } -OverwritePropertyName 'CountryCode' 11 | ) 12 | 13 | $Users = Find-PasswordQuality -Replacements $Replacements 14 | $Users | Format-Table -------------------------------------------------------------------------------- /Examples/Example-FindUsersPasswordQualityReport.ps1: -------------------------------------------------------------------------------- 1 | Clear-Host 2 | Import-Module .\PasswordSolution.psd1 -Force 3 | 4 | # option 1, one-liner 5 | # Show-PasswordQuality -FilePath C:\Temp\PasswordQuality.html -Online -WeakPasswords "Test1", "Test2", "Test3" -Verbose -SeparateDuplicateGroups -AddWorldMap -PassThru 6 | 7 | # option 2, for easier reading with splatting 8 | $showPasswordQualitySplat = @{ 9 | FilePath = "$PSScriptRoot\Reporting\PasswordQuality_$(Get-Date -f yyyy-MM-dd_HHmmss).html" 10 | WeakPasswords = "Test1", "Test2", "Test3", 'February2023!#!@ok', $Passwords | ForEach-Object { $_ } 11 | WeakPasswordsHashesFile = 'C:\Support\GitHub\PwnedDatabaseDownloader\pwnedpasswords_ntlm.txt' 12 | WeakPasswordsHashesSortedFile = 'C:\Support\GitHub\PwnedDatabaseDownloader\pwnedpasswords_ntlm.txt' 13 | SeparateDuplicateGroups = $true 14 | PassThru = $true 15 | AddWorldMap = $true 16 | LogPath = "$PSScriptRoot\Logs\PasswordQuality_$(Get-Date -f yyyy-MM-dd_HHmmss).log" 17 | Online = $true 18 | LogMaximum = 5 19 | } 20 | 21 | Show-PasswordQuality @showPasswordQualitySplat -Verbose -------------------------------------------------------------------------------- /Examples/Example-FindUsersPasswordQualityReportWithReplacements.ps1: -------------------------------------------------------------------------------- 1 | Import-Module .\PasswordSolution.psd1 -Force 2 | 3 | $showPasswordQualitySplat = @{ 4 | FilePath = "$PSScriptRoot\Reporting\PasswordQuality_$(Get-Date -f yyyy-MM-dd_HHmmss).html" 5 | WeakPasswords = "Test1", "Test2", "Test3", 'February2023!#!@ok', $Passwords | ForEach-Object { $_ } 6 | SeparateDuplicateGroups = $true 7 | PassThru = $true 8 | AddWorldMap = $true 9 | LogPath = "$PSScriptRoot\Logs\PasswordQuality_$(Get-Date -f yyyy-MM-dd_HHmmss).log" 10 | Online = $true 11 | LogMaximum = 5 12 | Replacements = New-PasswordConfigurationReplacement -PropertyName 'ExtensionAttribute4' -Type eq -PropertyReplacementHash @{ 13 | 'PL' = 'Poland' 14 | 'DE' = 'Germany' 15 | 'AT' = 'Austria' 16 | 'IT' = 'Italy' 17 | 'Unknown' = 'Not specified in AD' 18 | } -OverwritePropertyName 'CountriesByExtension' 19 | GroupBy = 'DelegatableAdmins' 20 | } 21 | 22 | Show-PasswordQuality @showPasswordQualitySplat -Verbose -------------------------------------------------------------------------------- /Examples/Example-PasswordDashboard.ps1: -------------------------------------------------------------------------------- 1 | Import-Module .\PasswordSolution.psd1 -Force 2 | 3 | $Passwords = Find-Password 4 | 5 | New-HTML { 6 | New-TableOption -DataStore JavaScript -ArrayJoin -BoolAsString 7 | New-HTMLTable -DataTable $Passwords -SearchBuilder -Filtering { 8 | 9 | } 10 | } -ShowHTML -------------------------------------------------------------------------------- /Examples/Example-ReRegisterTaskAsGMSA.ps1: -------------------------------------------------------------------------------- 1 | $Tasks = Get-ScheduledTask -TaskPath "\" -TaskName "Automated-PasswordSolution" 2 | 3 | # Fix all tasks to use proper account 4 | foreach ($Task in $Tasks) { 5 | schtasks /Change /TN $Task.TaskName /RU "GMSA$" /RP "" 6 | } 7 | foreach ($Task in $Tasks) { 8 | #Start-ScheduledTask -TaskName $Task.TaskName -Verbose 9 | } -------------------------------------------------------------------------------- /Examples/Example-SearchNotification.ps1: -------------------------------------------------------------------------------- 1 | Import-Module .\PasswordSolution.psd1 -Force 2 | 3 | Find-PasswordNotification -SearchPath "$PSScriptRoot\Search\SearchLog_2022-08.xml" | Format-Table 4 | #Find-PasswordNotification -SearchPath "$PSScriptRoot\Search\SearchLog_2021-06.xml" -Manager | Format-Table -------------------------------------------------------------------------------- /Examples/WeakPasswordGenerator.ps1: -------------------------------------------------------------------------------- 1 | $Months = @( 2 | # english 3 | "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" 4 | # polish 5 | "Styczen", "Luty", "Marzec", "Kwiecien", "Maj", "Czerwiec", "Lipiec", "Sierpien", "Wrzesien", "Pazdziernik", "Listopad", "Grudzien" 6 | # spanish 7 | 'Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Setiembre', 'Octubre', 'Noviembre', 'Diciembre' 8 | # german 9 | "Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember" 10 | # russian 11 | "Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь" 12 | # french 13 | 'Janvier', 'Fevrier', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Aout', 'Septembre', 'Octobre', 'Novembre', 'Decembre' 14 | ) | Sort-Object -Unique 15 | $Numbers = 0..9 16 | $Years = 2020..2023 17 | $SpecialChar = @("!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "-", "+", "=", "[", "]", "{", "}", "|", "\") 18 | 19 | $Passwords = foreach ($Year in $Years) { 20 | Write-Color -Text "Year: ", $Year -Color Yellow, White 21 | $YearPasswords = foreach ($month in $months) { 22 | foreach ($number in $numbers) { 23 | foreach ($special in $SpecialChar) { 24 | $month + $Year.ToString() + $number.ToString() + $special 25 | $Year.ToString() + $month + $number.ToString() + $special 26 | $month + $Year.ToString() + $special 27 | } 28 | } 29 | } 30 | Write-Color -Text "Year: ", $Year, " passwords created: ", $YearPasswords.Count -Color Yellow, White 31 | $YearPasswords 32 | } 33 | $Passwords.Count -------------------------------------------------------------------------------- /PasswordSolution.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | AliasesToExport = @() 3 | Author = 'Przemyslaw Klys' 4 | CmdletsToExport = @() 5 | CompanyName = 'Evotec' 6 | CompatiblePSEditions = @('Desktop', 'Core') 7 | Copyright = '(c) 2011 - 2025 Przemyslaw Klys @ Evotec. All rights reserved.' 8 | Description = 'This module allows the creation of password expiry emails for users, managers, administrators, and security according to defined templates. It''s able to work with different rules allowing to fully customize who gets the email and when.' 9 | FunctionsToExport = @('Find-Password', 'Find-PasswordEntra', 'Find-PasswordNotification', 'Find-PasswordQuality', 'New-PasswordConfigurationEmail', 'New-PasswordConfigurationEntra', 'New-PasswordConfigurationExternalUsers', 'New-PasswordConfigurationOption', 'New-PasswordConfigurationReplacement', 'New-PasswordConfigurationReport', 'New-PasswordConfigurationRule', 'New-PasswordConfigurationRuleReminder', 'New-PasswordConfigurationTemplate', 'New-PasswordConfigurationType', 'Show-PasswordQuality', 'Start-PasswordSolution') 10 | GUID = 'c58ff818-1de6-4500-961c-a243c2043255' 11 | ModuleVersion = '2.1.0' 12 | PowerShellVersion = '5.1' 13 | PrivateData = @{ 14 | PSData = @{ 15 | ExternalModuleDependencies = @('ActiveDirectory') 16 | IconUri = 'https://evotec.xyz/wp-content/uploads/2022/08/PasswordSolution.png' 17 | ProjectUri = 'https://github.com/EvotecIT/PasswordSolution' 18 | Tags = @('password', 'passwordexpiry', 'activedirectory', 'windows') 19 | } 20 | } 21 | RequiredModules = @(@{ 22 | Guid = 'ee272aa8-baaa-4edf-9f45-b6d6f7d844fe' 23 | ModuleName = 'PSSharedGoods' 24 | ModuleVersion = '0.0.307' 25 | }, @{ 26 | Guid = 'a7bdf640-f5cb-4acf-9de0-365b322d245c' 27 | ModuleName = 'PSWriteHTML' 28 | ModuleVersion = '1.28.0' 29 | }, @{ 30 | Guid = '0b0ba5c5-ec85-4c2b-a718-874e55a8bc3f' 31 | ModuleName = 'PSWriteColor' 32 | ModuleVersion = '1.0.1' 33 | }, @{ 34 | Guid = '2b0ea9f1-3ff1-4300-b939-106d5da608fa' 35 | ModuleName = 'Mailozaurr' 36 | ModuleVersion = '1.0.0' 37 | }, 'ActiveDirectory') 38 | RootModule = 'PasswordSolution.psm1' 39 | } -------------------------------------------------------------------------------- /PasswordSolution.psm1: -------------------------------------------------------------------------------- 1 | #Get public and private function definition files. 2 | $Public = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue -Recurse ) 3 | $Private = @( Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue -Recurse ) 4 | $Classes = @( Get-ChildItem -Path $PSScriptRoot\Classes\*.ps1 -ErrorAction SilentlyContinue -Recurse ) 5 | $Enums = @( Get-ChildItem -Path $PSScriptRoot\Enums\*.ps1 -ErrorAction SilentlyContinue -Recurse ) 6 | 7 | $AssemblyFolders = Get-ChildItem -Path $PSScriptRoot\Lib -Directory -ErrorAction SilentlyContinue 8 | if ($AssemblyFolders.BaseName -contains 'Standard') { 9 | $Assembly = @( Get-ChildItem -Path $PSScriptRoot\Lib\Standard\*.dll -ErrorAction SilentlyContinue ) 10 | } else { 11 | if ($PSEdition -eq 'Core') { 12 | $Assembly = @( Get-ChildItem -Path $PSScriptRoot\Lib\Core\*.dll -ErrorAction SilentlyContinue ) 13 | } else { 14 | $Assembly = @( Get-ChildItem -Path $PSScriptRoot\Lib\Default\*.dll -ErrorAction SilentlyContinue ) 15 | } 16 | } 17 | $FoundErrors = @( 18 | Foreach ($Import in @($Assembly)) { 19 | try { 20 | Add-Type -Path $Import.Fullname -ErrorAction Stop 21 | } catch [System.Reflection.ReflectionTypeLoadException] { 22 | Write-Warning "Processing $($Import.Name) Exception: $($_.Exception.Message)" 23 | $LoaderExceptions = $($_.Exception.LoaderExceptions) | Sort-Object -Unique 24 | foreach ($E in $LoaderExceptions) { 25 | Write-Warning "Processing $($Import.Name) LoaderExceptions: $($E.Message)" 26 | } 27 | $true 28 | #Write-Error -Message "StackTrace: $($_.Exception.StackTrace)" 29 | } catch { 30 | Write-Warning "Processing $($Import.Name) Exception: $($_.Exception.Message)" 31 | $LoaderExceptions = $($_.Exception.LoaderExceptions) | Sort-Object -Unique 32 | foreach ($E in $LoaderExceptions) { 33 | Write-Warning "Processing $($Import.Name) LoaderExceptions: $($E.Message)" 34 | } 35 | $true 36 | #Write-Error -Message "StackTrace: $($_.Exception.StackTrace)" 37 | } 38 | } 39 | #Dot source the files 40 | Foreach ($Import in @($Private + $Public + $Classes + $Enums)) { 41 | Try { 42 | . $Import.Fullname 43 | } Catch { 44 | Write-Error -Message "Failed to import functions from $($import.Fullname): $_" 45 | $true 46 | } 47 | } 48 | ) 49 | 50 | if ($FoundErrors.Count -gt 0) { 51 | $ModuleName = (Get-ChildItem $PSScriptRoot\*.psd1).BaseName 52 | Write-Warning "Importing module $ModuleName failed. Fix errors before continuing." 53 | break 54 | } 55 | 56 | Export-ModuleMember -Function '*' -Alias '*' -------------------------------------------------------------------------------- /Private/Add-ManagerInformation.ps1: -------------------------------------------------------------------------------- 1 | function Add-ManagerInformation { 2 | [CmdletBinding()] 3 | param( 4 | [System.Collections.IDictionary] $SummaryDictionary, 5 | [string] $Type, 6 | [string] $ManagerType, 7 | [Object] $Key, 8 | [PSCustomObject] $User, 9 | [PSCustomObject] $Rule, 10 | [System.Collections.IDictionary] $Entra 11 | ) 12 | if ($Key) { 13 | if ($Entra.Enabled) { 14 | # If entra is enabled we can use UserPrincipalName 15 | $UserSearchString = $User.UserPrincipalName 16 | if ($Key -is [string]) { 17 | $KeyDN = $Key 18 | } else { 19 | $KeyDN = $Key.AdditionalProperties.displayName 20 | } 21 | } else { 22 | $UserSearchString = $User.DistinguishedName 23 | if ($Key -is [string]) { 24 | $KeyDN = $Key 25 | } else { 26 | $KeyDN = $Key.DisplayName 27 | } 28 | } 29 | if (-not $SummaryDictionary[$KeyDN]) { 30 | $SummaryDictionary[$KeyDN] = [ordered] @{ 31 | Manager = $Key 32 | ManagerDefault = [ordered] @{} 33 | ManagerNotCompliant = [ordered] @{} 34 | Security = [ordered] @{} 35 | } 36 | } 37 | $SummaryDictionary[$KeyDN][$Type][$UserSearchString] = [ordered] @{ 38 | Manager = $User.ManagerDN 39 | User = $User 40 | Rule = $Rule 41 | ManagerOption = $Type 42 | Output = [ordered] @{} 43 | } 44 | $Default = [ordered] @{ 45 | DisplayName = $User.DisplayName 46 | Enabled = $User.Enabled 47 | SamAccountName = $User.SamAccountName 48 | Domain = $User.Domain 49 | DateExpiry = $User.DateExpiry 50 | DaysToExpire = $User.DaysToExpire 51 | PasswordLastSet = $User.PasswordLastSet 52 | PasswordExpired = $User.PasswordExpired 53 | } 54 | if ($Type -ne 'ManagerDefault') { 55 | $Extended = [ordered] @{ 56 | 'Status' = $ManagerType 57 | 'Manager' = $User.Manager 58 | 'Manager Email' = $User.ManagerEmail 59 | } 60 | $SummaryDictionary[$KeyDN][$Type][$UserSearchString]['Output'] = [PSCustomObject] ( $Extended + $Default) 61 | } else { 62 | $SummaryDictionary[$KeyDN][$Type][$UserSearchString]['Output'] = [PSCustomObject] $Default 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /Private/Add-ParametersToString.ps1: -------------------------------------------------------------------------------- 1 | function Add-ParametersToString { 2 | <# 3 | .SYNOPSIS 4 | Short description 5 | 6 | .DESCRIPTION 7 | Long description 8 | 9 | .PARAMETER String 10 | Parameter description 11 | 12 | .PARAMETER Parameter 13 | Parameter description 14 | 15 | .EXAMPLE 16 | $Test = 'this is a string $Test - and $Test2 AND $tEST3' 17 | 18 | Add-ParametersToString -String $Test -Parameter @{ 19 | Testooo = 'sdsds' 20 | Test = 'oh my god' 21 | Test2 = 'ole ole' 22 | TEST3 = '56555' 23 | } 24 | 25 | .NOTES 26 | General notes 27 | #> 28 | [CmdletBinding()] 29 | param( 30 | [string] $String, 31 | [System.Collections.IDictionary] $Parameter 32 | ) 33 | $Sorted = $Parameter.Keys | Sort-Object { $_.length } -Descending 34 | 35 | 36 | foreach ($Key in $Sorted) { 37 | $String = $String -ireplace [Regex]::Escape("`$$Key"), $Parameter[$Key] 38 | } 39 | $String 40 | } -------------------------------------------------------------------------------- /Private/Export-SearchInformation.ps1: -------------------------------------------------------------------------------- 1 | function Export-SearchInformation { 2 | [CmdletBinding()] 3 | param( 4 | [string] $SearchPath, 5 | [System.Collections.IDictionary] $SummarySearch, 6 | [string] $Today, 7 | [Array] $SummaryUsersEmails, 8 | [Array] $SummaryManagersEmails, 9 | [Array] $SummaryEscalationEmails 10 | ) 11 | 12 | if ($SearchPath) { 13 | Write-Color -Text "[i]" , " Saving Search report " -Color White, Yellow, Green 14 | if ($SummaryUsersEmails) { 15 | $SummarySearch['EmailSent'][$Today] += $SummaryUsersEmails 16 | } 17 | if ($SummaryEscalationEmails) { 18 | $SummarySearch['EmailEscalations'][$Today] += $SummaryEscalationEmails 19 | } 20 | if ($SummaryManagersEmails) { 21 | $SummarySearch['EmailManagers'][$Today] += $SummaryManagersEmails 22 | } 23 | try { 24 | $SummarySearch | Export-Clixml -LiteralPath $SearchPath -ErrorAction Stop 25 | } catch { 26 | Write-Color -Text "[e]", " Couldn't save to file $SearchPath", ". Error: ", $_.Exception.Message -Color White, Yellow, White, Yellow, White, Yellow, White 27 | } 28 | Write-Color -Text "[i]" , " Saving Search report ", "Done" -Color White, Yellow, Green 29 | } 30 | } -------------------------------------------------------------------------------- /Private/Fromat-ReminderDays.ps1: -------------------------------------------------------------------------------- 1 | function Format-ReminderDays { 2 | <# 3 | .SYNOPSIS 4 | Formats an array of reminder days into a readable, concise format. 5 | 6 | .DESCRIPTION 7 | This function accepts an array of numbers (which may include nested arrays) 8 | and always sorts them in ascending order. It then groups contiguous sequences 9 | (where each subsequent number is either equal to or exactly 1 greater than the previous) 10 | and formats groups of three or more unique numbers as a range. For clarity, if any 11 | number in the range is negative the range is displayed using " to " (e.g. "-500 to 500") 12 | to avoid confusion with hyphenated negatives. 13 | 14 | .PARAMETER Days 15 | An array of integers (or nested arrays of integers) representing reminder days. 16 | 17 | .EXAMPLE 18 | Format-ReminderDays -Days @(1,2,3,7,30) 19 | # Returns: "1-3, 7, 30" 20 | 21 | .EXAMPLE 22 | $xxx = @(500..-500), 60, 59, 30, 15, 7, 3, 2, 1, 0, -7, -15, -30, -45, 600, -505 23 | Format-ReminderDays -Days $xxx 24 | # Returns: "-505, -500 to 500, 600" 25 | 26 | .EXAMPLE 27 | Format-ReminderDays -Days @(15..-500), 60, 59, 30, 15, 7, 3, 2, 1, 0, -7, -15, -30, -45, 600, -505 28 | # Returns: -505, -500 to 15, 30, 59, 60, 600 29 | #> 30 | [CmdletBinding()] 31 | param( 32 | [Parameter(Mandatory = $true)] 33 | [Array]$Days 34 | ) 35 | 36 | # Flatten the input array (to handle nested arrays like those produced by ranges) 37 | $flatDays = @() 38 | foreach ($item in $Days) { 39 | if ($item -is [Array]) { 40 | $flatDays += $item 41 | } else { 42 | $flatDays += $item 43 | } 44 | } 45 | 46 | # Convert all items to integers. 47 | $flatDays = $flatDays | ForEach-Object { [int]$_ } 48 | 49 | # Always sort in ascending order. 50 | $sortedDays = $flatDays | Sort-Object 51 | 52 | # Group contiguous numbers. 53 | $groups = @() 54 | $currentGroup = @($sortedDays[0]) 55 | for ($i = 1; $i -lt $sortedDays.Count; $i++) { 56 | $current = $sortedDays[$i] 57 | $previous = $sortedDays[$i - 1] 58 | # Group if the current number is equal (duplicate) or exactly 1 greater than the previous. 59 | if ($current -eq $previous -or $current -eq $previous + 1) { 60 | $currentGroup += $current 61 | } else { 62 | $groups += , @($currentGroup) 63 | $currentGroup = @($current) 64 | } 65 | } 66 | $groups += , @($currentGroup) 67 | 68 | # Format each group into a string. 69 | $formattedGroups = foreach ($group in $groups) { 70 | # Count unique numbers in the group. 71 | $uniqueCount = ($group | Select-Object -Unique).Count 72 | if ($uniqueCount -ge 3) { 73 | # Use " to " when any value is negative (for clarity) 74 | if ($group[0] -lt 0 -or $group[-1] -lt 0) { 75 | "$($group[0]) to $($group[-1])" 76 | } else { 77 | "$($group[0])-$($group[-1])" 78 | } 79 | } else { 80 | # For groups with fewer than 3 unique numbers, list the numbers separated by commas. 81 | $group -join ", " 82 | } 83 | } 84 | 85 | return ($formattedGroups -join ", ") 86 | } -------------------------------------------------------------------------------- /Private/Import-SearchInformation.ps1: -------------------------------------------------------------------------------- 1 | function Import-SearchInformation { 2 | [CmdletBinding()] 3 | param( 4 | [string] $SearchPath 5 | ) 6 | if ($SearchPath) { 7 | if (Test-Path -LiteralPath $SearchPath) { 8 | try { 9 | Write-Color -Text "[i]", " Loading file ", $SearchPath -Color White, Yellow, White, Yellow, White, Yellow, White 10 | $SummarySearch = Import-Clixml -LiteralPath $SearchPath -ErrorAction Stop 11 | } catch { 12 | Write-Color -Text "[e]", " Couldn't load the file $SearchPath", ". Skipping...", $_.Exception.Message -Color White, Yellow, White, Yellow, White, Yellow, White 13 | } 14 | } 15 | } 16 | if (-not $SummarySearch) { 17 | $SummarySearch = [ordered] @{ 18 | EmailSent = [ordered] @{} 19 | EmailManagers = [ordered] @{} 20 | EmailEscalations = [ordered] @{} 21 | } 22 | } 23 | $SummarySearch 24 | 25 | } -------------------------------------------------------------------------------- /Private/Send-PasswordAdminNotifications.ps1: -------------------------------------------------------------------------------- 1 | function Send-PasswordAdminNotifications { 2 | [CmdletBinding()] 3 | param( 4 | [System.Collections.IDictionary] $AdminSection, 5 | [scriptblock] $TemplateAdmin, 6 | [string] $TemplateAdminSubject, 7 | [string] $TimeEnd, 8 | [System.Collections.IDictionary] $EmailParameters, 9 | [Array] $HtmlAttachments, 10 | [System.Collections.IDictionary] $Logging 11 | ) 12 | 13 | if ($AdminSection.Enable) { 14 | Write-Color -Text "[i] Sending summary information " -Color White, Yellow, White, Yellow, White, Yellow, White 15 | $CountSecurity = 0 16 | [Array] $SummaryEmail = @( 17 | $CountSecurity++ 18 | # This user is provided by user in config file 19 | $ManagerUser = $AdminSection.Manager 20 | 21 | $EmailSplat = [ordered] @{} 22 | # User uses global template 23 | $EmailSplat.Template = $TemplateAdmin 24 | $EmailSplat.Subject = $TemplateAdminSubject 25 | $EmailSplat.User = $ManagerUser 26 | 27 | $EmailSplat.SummaryUsersEmails = $SummaryUsersEmails 28 | $EmailSplat.SummaryManagersEmails = $SummaryManagersEmails 29 | $EmailSplat.SummaryEscalationEmails = $SummaryEscalationEmails 30 | $EmailSplat.TimeToProcess = $TimeEnd 31 | 32 | $EmailSplat.EmailParameters = $EmailParameters 33 | 34 | $EmailSplat.EmailParameters.To = $AdminSection.Manager.EmailAddress 35 | 36 | $EmailSplat.EmailDateFormat = $Logging.EmailDateFormat 37 | $EmailSplat.EmailDateFormatUTCConversion = $Logging.EmailDateFormatUTCConversion 38 | 39 | if ($HtmlAttachments.Count -gt 0) { 40 | $EmailSplat.Attachments = $HtmlAttachments 41 | } 42 | Write-Color -Text "[i] Sending summary information ", $ManagerUser.DisplayName, " (", $ManagerUser.EmailAddress, ")" -Color White, Yellow, White, Yellow, White, Yellow, White, Yellow, White, Yellow 43 | 44 | $EmailResult = Send-PasswordEmail @EmailSplat 45 | 46 | if ($EmailResult.Error) { 47 | Write-Color -Text "[r] Sending summary information ", $ManagerUser.DisplayName, " (", $ManagerUser.EmailAddress, ") ) (status: ", $EmailResult.Status, ", sent to: ", $EmailResult.SentTo, ", error: ", $EmailResult.Error, ")" -Color White, Yellow, White, Yellow, White, Yellow, White, Yellow, White, Yellow 48 | } else { 49 | Write-Color -Text "[r] Sending summary information ", $ManagerUser.DisplayName, " (", $ManagerUser.EmailAddress, ") ) (status: ", $EmailResult.Status, ", sent to: ", $EmailResult.SentTo, ")" -Color White, Yellow, White, Yellow, White, Yellow, White, Yellow, White, Yellow 50 | } 51 | [PSCustomObject] @{ 52 | DisplayName = $ManagerUser.DisplayName 53 | SamAccountName = $ManagerUser.SamAccountName 54 | Domain = $ManagerUser.Domain 55 | Status = $EmailResult.Status 56 | StatusWhen = Get-Date -Format "yyyy-MM-dd HH:mm:ss" 57 | SentTo = $EmailResult.SentTo 58 | StatusError = $EmailResult.Error 59 | Template = 'Unknown' 60 | } 61 | ) 62 | Write-Color -Text "[i] Sending summary information (sent: ", $SummaryEmail.Count, ")" -Color White, Yellow, White, Yellow, White, Yellow, White 63 | } else { 64 | Write-Color -Text "[i] Sending summary information is ", "disabled!" -Color White, Yellow, DarkMagenta 65 | } 66 | } -------------------------------------------------------------------------------- /Private/Send-PasswordEmail.ps1: -------------------------------------------------------------------------------- 1 | function Send-PasswordEmail { 2 | [CmdletBinding()] 3 | param( 4 | [scriptblock] $Template, 5 | [PSCustomObject] $User, 6 | [Array] $ManagedUsers, 7 | [Array] $ManagedUsersManagerNotCompliant, 8 | [Array] $SummaryUsersEmails, 9 | [Array] $SummaryManagersEmails, 10 | [Array] $SummaryEscalationEmails, 11 | [string] $TimeToProcess, 12 | [Array] $Attachments, 13 | [System.Collections.IDictionary] $EmailParameters, 14 | [string] $Subject, 15 | [string] $EmailDateFormat, 16 | [switch] $EmailDateFormatUTCConversion 17 | ) 18 | 19 | if ($Template) { 20 | if ($User.PasswordLastSet) { 21 | if ($EmailDateFormat) { 22 | if ($EmailDateFormatUTCConversion) { 23 | $PasswordLastSet = $User.PasswordLastSet.ToUniversalTime().ToString($EmailDateFormat) 24 | } else { 25 | $PasswordLastSet = $User.PasswordLastSet.ToString($EmailDateFormat) 26 | } 27 | } else { 28 | if ($EmailDateFormatUTCConversion) { 29 | $PasswordLastSet = $User.PasswordLastSet.ToUniversalTime() 30 | } else { 31 | $PasswordLastSet = $User.PasswordLastSet 32 | } 33 | } 34 | } else { 35 | $PasswordLastSet = $User.PasswordLastSet 36 | } 37 | if ($User.DateExpiry) { 38 | if ($EmailDateFormat) { 39 | if ($EmailDateFormatUTCConversion) { 40 | $ExpiryDate = $User.DateExpiry.ToUniversalTime().ToString($EmailDateFormat) 41 | } else { 42 | $ExpiryDate = $User.DateExpiry.ToString($EmailDateFormat) 43 | } 44 | } else { 45 | if ($EmailDateFormatUTCConversion) { 46 | $ExpiryDate = $User.DateExpiry.ToUniversalTime() 47 | } else { 48 | $ExpiryDate = $User.DateExpiry 49 | } 50 | } 51 | } else { 52 | $ExpiryDate = $User.DateExpiry 53 | } 54 | 55 | # Simplify counting for a user used variables 56 | $CountUserEmails = 0 57 | $CountUserEmailsSent = 0 58 | $CountUserEmailsNotSentLackOfEmail = 0 59 | $CountUserEmailsNotSentOther = 0 60 | foreach ($User in $SummaryUsersEmails) { 61 | $CountUserEmails++ 62 | if ($User.Status -eq $true) { 63 | $CountUserEmailsSent++ 64 | } else { 65 | if ($User.StatusError -eq 'No email address for user') { 66 | $CountUserEmailsNotSentLackOfEmail++ 67 | } else { 68 | $CountUserEmailsNotSentOther++ 69 | } 70 | } 71 | } 72 | $CountManagerEmails = $SummaryManagersEmails.Count 73 | $CountEscalationEmails = $SummaryEscalationEmails.Count 74 | 75 | $SourceParameters = [ordered] @{ 76 | ManagerDisplayName = $User.DisplayName 77 | ManagerUsersTable = $ManagedUsers 78 | ManagerUsersTableManagerNotCompliant = $ManagedUsersManagerNotCompliant 79 | SummaryEscalationEmails = $SummaryEscalationEmails 80 | SummaryManagersEmails = $SummaryManagersEmails 81 | SummaryUsersEmails = $SummaryUsersEmails 82 | TimeToProcess = $TimeToProcess 83 | 84 | # Summary counting 85 | CountUserEmails = $CountUserEmails 86 | CountUserEmailsSent = $CountUserEmailsSent 87 | CountUserEmailsNotSentLackOfEmail = $CountUserEmailsNotSentLackOfEmail 88 | CountUserEmailsNotSentOther = $CountUserEmailsNotSentOther 89 | CountManagerEmails = $CountManagerEmails 90 | CountEscalationEmails = $CountEscalationEmails 91 | 92 | # Only works if User is set 93 | UserPrincipalName = $User.UserPrincipalName # : adm.pklys@ad.evotec.xyz 94 | SamAccountName = $User.SamAccountName # : adm.pklys 95 | Domain = $User.Domain # : ad.evotec.xyz 96 | Enabled = $User.Enabled 97 | EmailAddress = $User.EmailAddress # : 98 | DateExpiry = $ExpiryDate # : 99 | DaysToExpire = $User.DaysToExpire # : 100 | PasswordExpired = $User.PasswordExpired # : False 101 | PasswordLastSet = $PasswordLastSet # : 05.09.2020 11:07:29 102 | PasswordNotRequired = $User.PasswordNotRequired # : False 103 | PasswordNeverExpires = $User.PasswordNeverExpires # : True 104 | ManagerSamAccountName = $User.ManagerSamAccountName # : przemyslaw.klys 105 | ManagerEmail = $User.ManagerEmail # : przemyslaw.klys@test.pl 106 | ManagerStatus = $User.ManagerStatus # : Enabled 107 | ManagerLastLogonDays = $User.ManagerLastLogonDays # : 0 108 | Manager = $User.Manager # : Przemysław Kłys 109 | DisplayName = $User.DisplayName # : Administrator Przemysław Kłys 110 | GivenName = $User.GivenName # : Administrator Przemysław 111 | Surname = $User.Surname # : Kłys 112 | OrganizationalUnit = $User.OrganizationalUnit # : OU=Special,OU=Accounts,OU=Production,DC=ad,DC=evotec,DC=xyz 113 | MemberOf = $User.MemberOf # : {CN=GDS-TestGroup4,OU=Security,OU=Groups,OU=Production,DC=ad,DC=evotec,DC=xyz, CN=GDS-TestGroup2,OU=Security,OU=Groups,OU=Production,DC=ad,DC=evotec,DC=xyz, CN=Domain Admins,CN=Users,DC=ad,DC=evotec,DC=xyz} 114 | DistinguishedName = $User.DistinguishedName # : CN=Administrator Przemysław Kłys,OU=Special,OU=Accounts,OU=Production,DC=ad,DC=evotec,DC=xyz 115 | ManagerDN = $User.ManagerDN # : CN=Przemysław Kłys,OU=Users,OU=Accounts,OU=Production,DC=ad,DC=evotec,DC=xyz 116 | } 117 | $Body = EmailBody -EmailBody $Template -Parameter $SourceParameters 118 | 119 | # Below command would require to define variables as they are used in scriptblock 120 | #$EmailParameters.Subject = $ExecutionContext.InvokeCommand.ExpandString($Subject) 121 | # following replacement is a bit more cumbersome the the one above but a bit more secure and doesn't require creating 20+ unused variables 122 | $EmailParameters.Subject = Add-ParametersToString -String $Subject -Parameter $SourceParameters 123 | $EmailParameters.Body = $Body 124 | if ($Attachments) { 125 | $EmailParameters.Attachment = $Attachments 126 | } else { 127 | $EmailParameters.Attachment = @() 128 | } 129 | try { 130 | Send-EmailMessage @EmailParameters -ErrorAction Stop -WarningAction SilentlyContinue 131 | } catch { 132 | if ($_.Exception.Message -like "*Credential*") { 133 | Write-Color -Text "[e] " , "Failed to send email to $($EmailParameters.EmailParameters) because error: $($_.Exception.Message)" -Color Yellow, White, Red 134 | Write-Color -Text "[i] " , "Please make sure you have valid credentials in your configuration file (graph encryption issue?)" -Color Yellow, White, Red 135 | } else { 136 | Write-Color -Text "[e] " , "Failed to send email to $($EmailParameters.EmailParameters) because error: $($_.Exception.Message)" -Color Yellow, White, Red 137 | } 138 | } 139 | } 140 | } -------------------------------------------------------------------------------- /Private/Send-PasswordManagerNotifications.ps1: -------------------------------------------------------------------------------- 1 | function Send-PasswordManagerNofifications { 2 | [CmdletBinding()] 3 | param( 4 | [System.Collections.IDictionary] $ManagerSection, 5 | [System.Collections.IDictionary] $Summary, 6 | [System.Collections.IDictionary] $CachedUsers, 7 | [ScriptBlock] $TemplateManager, 8 | [string] $TemplateManagerSubject, 9 | [ScriptBlock] $TemplateManagerNotCompliant, 10 | [string] $TemplateManagerNotCompliantSubject, 11 | [System.Collections.IDictionary] $EmailParameters, 12 | [System.Collections.IDictionary] $Logging, 13 | [System.Collections.IDictionary] $GlobalManagersCache 14 | ) 15 | if ($ManagerSection.Enable) { 16 | Write-Color -Text "[i] Sending notifications to managers " -Color White, Yellow, White, Yellow, White, Yellow, White 17 | $CountManagers = 0 18 | [Array] $SummaryManagersEmails = foreach ($Manager in $Summary['NotifyManager'].Keys) { 19 | $CountManagers++ 20 | if ($CachedUsers[$Manager]) { 21 | # This user is "findable" in AD 22 | $ManagerUser = $CachedUsers[$Manager] 23 | } elseif ($GlobalManagersCache[$Manager]) { 24 | # This user is findable in managers cache 25 | # This is required when user uses `FilterOrganizationalUnit` feature and manager is not in the same OU 26 | # This causes Manager Data to be not processed in the same way as User Data so we need to process it separately 27 | $ManagerUser = $GlobalManagersCache[$Manager] 28 | } else { 29 | # This user is provided by user in config file 30 | $ManagerUser = $Summary['NotifyManager'][$Manager]['Manager'] 31 | } 32 | [Array] $ManagedUsers = $Summary['NotifyManager'][$Manager]['ManagerDefault'].Values.Output 33 | [Array] $ManagedUsersManagerNotCompliant = $Summary['NotifyManager'][$Manager]['ManagerNotCompliant'].Values.Output 34 | 35 | $EmailSplat = [ordered] @{} 36 | 37 | if ($Summary['NotifyManager'][$Manager].ManagerDefault.Count -gt 0) { 38 | if ($TemplateManager) { 39 | # User uses global template 40 | $EmailSplat.Template = $TemplateManager 41 | } else { 42 | # User uses built-in template 43 | $EmailSplat.Template = { 44 | 45 | } 46 | } 47 | if ($TemplateManagerSubject) { 48 | $EmailSplat.Subject = $TemplateManagerSubject 49 | } else { 50 | $EmailSplat.Subject = "[Password Expiring] Dear Manager - Your accounts are expiring!" 51 | } 52 | } elseif ($Summary['NotifyManager'][$Manager].ManagerNotCompliant.Count -gt 0) { 53 | if ($TemplateManagerNotCompliant) { 54 | # User uses global template 55 | $EmailSplat.Template = $TemplateManagerNotCompliant 56 | } else { 57 | # User uses built-in template 58 | $EmailSplat.Template = { 59 | 60 | } 61 | } 62 | if ($TemplateManagerNotCompliantSubject) { 63 | $EmailSplat.Subject = $TemplateManagerNotCompliantSubject 64 | } else { 65 | $EmailSplat.Subject = "[Password Escalation] Accounts are expiring with non-compliant manager" 66 | } 67 | } 68 | 69 | $EmailSplat.User = $ManagerUser 70 | $EmailSplat.ManagedUsers = $ManagedUsers 71 | $EmailSplat.ManagedUsersManagerNotCompliant = $ManagedUsersManagerNotCompliant 72 | $EmailSplat.EmailParameters = $EmailParameters 73 | 74 | $EmailSplat.EmailDateFormat = $Logging.EmailDateFormat 75 | $EmailSplat.EmailDateFormatUTCConversion = $Logging.EmailDateFormatUTCConversion 76 | 77 | if ($ManagerSection.SendToDefaultEmail -ne $true) { 78 | $EmailSplat.EmailParameters.To = $ManagerUser.EmailAddress 79 | } else { 80 | $EmailSplat.EmailParameters.To = $ManagerSection.DefaultEmail 81 | } 82 | if ($Logging.NotifyOnManagerSend) { 83 | Write-Color -Text "[i] Sending notifications to managers ", $ManagerUser.DisplayName, " (", $ManagerUser.EmailAddress, ") (SendToDefaultEmail: ", $ManagerSection.SendToDefaultEmail, ")" -Color White, Yellow, White, Yellow, White, Yellow, White, Yellow, White, Yellow 84 | } 85 | [Array] $DisabledAccounts = foreach ($ManagedUserDN in $Summary['NotifyManager'][$Manager].ManagerDefault.Keys) { 86 | $AccountToDisable = [PSCustomObject] @{ 87 | SamAccountName = $ManagedUser.User.SamAccountName 88 | Domain = $ManagedUser.User.Domain 89 | Disabled = $null 90 | Error = $null 91 | } 92 | $ManagedUser = $Summary['NotifyManager'][$Manager].ManagerDefault[$ManagedUserDN] 93 | if ($ManagedUser.Rule -and $null -ne $ManagedUser.Rule.DisableDays) { 94 | $CompareSuccess = $false 95 | # We need to check if the user is in the disable days list 96 | if ($ManagedUser.Rule.DisableType -eq 'in') { 97 | $CompareSuccess = $ManagedUser.User.DaysToExpire -in $ManagedUser.Rule.DisableDays 98 | } elseif ($ManagedUser.Rule.DisableType -eq 'lt') { 99 | $CompareSuccess = $ManagedUser.User.DaysToExpire -lt $ManagedUser.Rule.DisableDays 100 | } elseif ($ManagedUser.Rule.DisableType -eq 'gt') { 101 | $CompareSuccess = $ManagedUser.User.DaysToExpire -gt $ManagedUser.Rule.DisableDays 102 | } elseif ($ManagedUser.Rule.DisableType -eq 'eq') { 103 | $CompareSuccess = $ManagedUser.User.DaysToExpire -eq $ManagedUser.Rule.DisableDays 104 | } else { 105 | Write-Color -Text "[r] Unknown disable type: ", $ManagedUser.Rule.DisableType, " for user ", $ManagedUser.User.DisplayName, " (", $ManagedUser.User.UserPrincipalName, ")" -Color White, Yellow, White, Yellow, White, Yellow, White, Yellow, White, Yellow 106 | } 107 | if ($CompareSuccess) { 108 | # Write-Color -Text "[i] Disabling user ", $ManagedUser.User.DisplayName, " (", $ManagedUser.User.UserPrincipalName, ") (DaysToExpire: ", $ManagedUser.User.DaysToExpire, ")" -Color Yellow, White, Magenta, White, Magenta, White, White, Blue 109 | #$ManagedUser.User 110 | if ($ManagedUser.Rule.DisableWhatIf) { 111 | Write-Color -Text "[i] Disabling user ", $ManagedUser.User.DisplayName, " (", $ManagedUser.User.UserPrincipalName, ")", " would be disabled" -Color Cyan, White, Red, Cyan, Red, Yellow 112 | $AccountToDisable.Disabled = $false 113 | $AccountToDisable.Error = "WhatIf" 114 | } else { 115 | # Disable the user 116 | Write-Color -Text "[i] Disabling user ", $ManagedUser.User.DisplayName, " (", $ManagedUser.User.UserPrincipalName, ")" -Color Cyan, White, Magenta, White, Magenta, White, White, Blue 117 | if ($ManagedUser.User.Enabled) { 118 | try { 119 | Disable-ADAccount -Identity $ManagedUser.User.DistinguishedName -Confirm:$false -WhatIf:$ManagedUser.Rule.DisableWhatIf -ErrorAction Stop 120 | $AccountToDisable.Disabled = $true 121 | $AccountToDisable.Error = $null 122 | } catch { 123 | $AccountToDisable.Disabled = $false 124 | $AccountToDisable.Error = $_.Exception.Message 125 | Write-Color -Text "[r] Disabling user ", $ManagedUser.User.DisplayName, " (", $ManagedUser.User.UserPrincipalName, ") failed: ", $_.Exception.Message -Color White, Yellow, White, Yellow, White, Yellow, White, Yellow, White, Yellow 126 | } 127 | } else { 128 | $AccountToDisable.Disabled = $false 129 | $AccountToDisable.Error = "Already disabled" 130 | Write-Color -Text "[i] User is already disabled: ", $ManagedUser.User.DisplayName, " (", $ManagedUser.User.UserPrincipalName, ")" -Color Cyan, White, Magenta, White, Magenta, White, White, Blue 131 | } 132 | } 133 | $AccountToDisable 134 | } 135 | } 136 | } 137 | $EmailResult = Send-PasswordEmail @EmailSplat 138 | if ($Logging.NotifyOnManagerSend) { 139 | if ($EmailResult.Error) { 140 | if ($EmailResult.SentTo) { 141 | Write-Color -Text "[r] Sending notifications to managers ", $ManagerUser.DisplayName, " (", $ManagerUser.EmailAddress, ") (SendToDefaultEmail: ", $ManagerSection.SendToDefaultEmail, ") (status: ", $EmailResult.Status, " sent to: ", $EmailResult.SentTo, ", error: ", $EmailResult.Error, ")" -Color White, Yellow, White, Yellow, White, Yellow, White, Yellow, White, Yellow, White, Yellow 142 | } else { 143 | Write-Color -Text "[r] Sending notifications to managers ", $ManagerUser.DisplayName, " (", $ManagerUser.EmailAddress, ") (SendToDefaultEmail: ", $ManagerSection.SendToDefaultEmail, ") (status: ", $EmailResult.Status, ", error: ", $EmailResult.Error, ")" -Color White, Yellow, White, Yellow, White, Yellow, White, Yellow, White, Yellow 144 | } 145 | } else { 146 | if ($EmailResult.SentTo) { 147 | Write-Color -Text "[r] Sending notifications to managers ", $ManagerUser.DisplayName, " (", $ManagerUser.EmailAddress, ") (SendToDefaultEmail: ", $ManagerSection.SendToDefaultEmail, ") (status: ", $EmailResult.Status, " sent to: ", $EmailResult.SentTo, ")" -Color White, Yellow, White, Yellow, White, Yellow, White, Yellow, White, Yellow 148 | } else { 149 | Write-Color -Text "[r] Sending notifications to managers ", $ManagerUser.DisplayName, " (", $ManagerUser.EmailAddress, ") (SendToDefaultEmail: ", $ManagerSection.SendToDefaultEmail, ") (status: ", $EmailResult.Status -Color White, Yellow, White, Yellow, White, Yellow, White, Yellow, White, Yellow 150 | } 151 | } 152 | } 153 | 154 | [PSCustomObject] @{ 155 | DisplayName = $ManagerUser.DisplayName 156 | SamAccountName = $ManagerUser.SamAccountName 157 | Domain = $ManagerUser.Domain 158 | Status = $EmailResult.Status 159 | StatusWhen = Get-Date -Format "yyyy-MM-dd HH:mm:ss" 160 | SentTo = $EmailResult.SentTo 161 | StatusError = $EmailResult.Error 162 | Accounts = $ManagedUsers.SamAccountName 163 | AccountsCount = $ManagedUsers.Count 164 | DisabledAccounts = $DisabledAccounts.SamAccountName 165 | DisabledAccountsCount = $DisabledAccounts.Count 166 | DisabledAccountsError = $DisabledAccounts.Error | Sort-Object -Unique 167 | Template = 'Unknown' 168 | ManagerNotCompliant = $ManagedUsersManagerNotCompliant.SamAccountName 169 | ManagerNotCompliantCount = $ManagedUsersManagerNotCompliant.Count 170 | #ManagerDisabled = $ManagedUsersManagerDisabled.SamAccountName 171 | #ManagerDisabledCount = $ManagedUsersManagerDisabled.Count 172 | #ManagerMissing = $ManagedUsersManagerMissing.SamAccountName 173 | #ManagerMissingCount = $ManagedUsersManagerMissing.Count 174 | #ManagerMissingEmail = $ManagedUsersManagerMissingEmail.SamAccountName 175 | #ManagerMissingEmailCount = $ManagedUsersManagerMissingEmail.Count 176 | } 177 | if ($ManagerSection.SendCountMaximum -gt 0) { 178 | if ($ManagerSection.SendCountMaximum -le $CountManagers) { 179 | Write-Color -Text "[i]", " Send count maximum reached. There may be more managers that match the rule." -Color Red, DarkRed 180 | break 181 | } 182 | } 183 | } 184 | Write-Color -Text "[i] Sending notifications to managers (sent: ", $SummaryManagersEmails.Count, " out of ", $Summary['NotifyManager'].Values.Count, ")" -Color White, Yellow, White, Yellow, White, Yellow, White 185 | $SummaryManagersEmails 186 | } else { 187 | Write-Color -Text "[i] Sending notifications to managers is ", "disabled!" -Color White, Yellow, DarkRed 188 | } 189 | } -------------------------------------------------------------------------------- /Private/Send-PasswordSecurityNotifications.ps1: -------------------------------------------------------------------------------- 1 | function Send-PasswordSecurityNotifications { 2 | [CmdletBinding()] 3 | param( 4 | [System.Collections.IDictionary] $SecuritySection, 5 | [System.Collections.IDictionary] $Summary, 6 | [ScriptBlock] $TemplateSecurity, 7 | [string] $TemplateSecuritySubject, 8 | [System.Collections.IDictionary] $Logging 9 | ) 10 | 11 | if ($SecuritySection.Enable) { 12 | Write-Color -Text "[i] Sending notifications to security " -Color White, Yellow, White, Yellow, White, Yellow, White 13 | $CountSecurity = 0 14 | [Array] $SummaryEscalationEmails = foreach ($Manager in $Summary['NotifySecurity'].Keys) { 15 | $CountSecurity++ 16 | # This user is provided by user in config file 17 | $ManagerUser = $Summary['NotifySecurity'][$Manager]['Manager'] 18 | 19 | [Array] $ManagedUsers = $Summary['NotifySecurity'][$Manager]['Security'].Values.Output 20 | 21 | $EmailSplat = [ordered] @{} 22 | 23 | if ($Summary['NotifySecurity'][$Manager].Security.Count -gt 0) { 24 | # User uses global template 25 | $EmailSplat.Template = $TemplateSecurity 26 | if ($TemplateSecuritySubject) { 27 | $EmailSplat.Subject = $TemplateSecuritySubject 28 | } else { 29 | $EmailSplat.Subject = "[Password Expiring] Dear Security - Accounts expired" 30 | } 31 | } else { 32 | continue 33 | } 34 | 35 | if ($SecuritySection.AttachCSV -and $ManagedUsers.Count -gt 0) { 36 | $ManagedUsers | Export-Csv -LiteralPath $Env:TEMP\ManagedUsersSecurity.csv -NoTypeInformation -Force -Encoding UTF8 -ErrorAction Stop 37 | $EmailSplat.Attachments = @( 38 | if (Test-Path -LiteralPath "$Env:TEMP\ManagedUsersSecurity.csv") { 39 | "$Env:TEMP\ManagedUsersSecurity.csv" 40 | } 41 | ) 42 | } 43 | $EmailSplat.User = $ManagerUser 44 | $EmailSplat.ManagedUsers = $ManagedUsers | Select-Object -Property 'Status', 'DisplayName', 'Enabled', 'SamAccountName', 'Domain', 'DateExpiry', 'DaysToExpire', 'PasswordLastSet', 'PasswordExpired' 45 | #$EmailSplat.ManagedUsersManagerNotCompliant = $ManagedUsersManagerNotCompliant 46 | #$EmailSplat.ManagedUsersManagerDisabled = $ManagedUsersManagerDisabled 47 | #$EmailSplat.ManagedUsersManagerMissing = $ManagedUsersManagerMissing 48 | #$EmailSplat.ManagedUsersManagerMissingEmail = $ManagedUsersManagerMissingEmail 49 | $EmailSplat.EmailParameters = $EmailParameters 50 | 51 | $EmailSplat.EmailDateFormat = $Logging.EmailDateFormat 52 | $EmailSplat.EmailDateFormatUTCConversion = $Logging.EmailDateFormatUTCConversion 53 | 54 | if ($SecuritySection.SendToDefaultEmail -ne $true) { 55 | $EmailSplat.EmailParameters.To = $ManagerUser.EmailAddress 56 | } else { 57 | $EmailSplat.EmailParameters.To = $SecuritySection.DefaultEmail 58 | } 59 | if ($Logging.NotifyOnSecuritySend) { 60 | Write-Color -Text "[i] Sending notifications to security ", $ManagerUser.DisplayName, " (", $ManagerUser.EmailAddress, ") (SendToDefaultEmail: ", $ManagerSection.SendToDefaultEmail, ")" -Color White, Yellow, White, Yellow, White, Yellow, White, Yellow, White, Yellow 61 | } 62 | $EmailResult = Send-PasswordEmail @EmailSplat 63 | if ($Logging.NotifyOnSecuritySend) { 64 | if ($EmailResult.Error) { 65 | Write-Color -Text "[r] Sending notifications to security ", $ManagerUser.DisplayName, " (", $ManagerUser.EmailAddress, ") (SendToDefaultEmail: ", $ManagerSection.SendToDefaultEmail, ") (status: ", $EmailResult.Status, " sent to: ", $EmailResult.SentTo, ", error: ", $EmailResult.Error, ")" -Color White, Yellow, White, Yellow, White, Yellow, White, Yellow, White, Yellow, White, Yellow 66 | } else { 67 | Write-Color -Text "[r] Sending notifications to security ", $ManagerUser.DisplayName, " (", $ManagerUser.EmailAddress, ") (SendToDefaultEmail: ", $ManagerSection.SendToDefaultEmail, ") (status: ", $EmailResult.Status, " sent to: ", $EmailResult.SentTo, ")" -Color White, Yellow, White, Yellow, White, Yellow, White, Yellow, White, Yellow 68 | } 69 | } 70 | [PSCustomObject] @{ 71 | DisplayName = $ManagerUser.DisplayName 72 | SamAccountName = $ManagerUser.SamAccountName 73 | Domain = $ManagerUser.Domain 74 | Status = $EmailResult.Status 75 | StatusWhen = Get-Date -Format "yyyy-MM-dd HH:mm:ss" 76 | SentTo = $EmailResult.SentTo 77 | StatusError = $EmailResult.Error 78 | Accounts = $ManagedUsers.SamAccountName 79 | AccountsCount = $ManagedUsers.Count 80 | Template = 'Unknown' 81 | # ManagerNotCompliant = $ManagedUsersManagerNotCompliant.SamAccountName 82 | # ManagerNotCompliantCount = $ManagedUsersManagerNotCompliant.Count 83 | #ManagerDisabled = $ManagedUsersManagerDisabled.SamAccountName 84 | #ManagerDisabledCount = $ManagedUsersManagerDisabled.Count 85 | #ManagerMissing = $ManagedUsersManagerMissing.SamAccountName 86 | #ManagerMissingCount = $ManagedUsersManagerMissing.Count 87 | #ManagerMissingEmail = $ManagedUsersManagerMissingEmail.SamAccountName 88 | #ManagerMissingEmailCount = $ManagedUsersManagerMissingEmail.Count 89 | } 90 | if ($SecuritySection.SendCountMaximum -gt 0) { 91 | if ($SecuritySection.SendCountMaximum -le $CountSecurity) { 92 | Write-Color -Text "[i]", " Send count maximum reached. There may be more managers that match the rule." -Color Red, DarkRed 93 | break 94 | } 95 | } 96 | } 97 | Write-Color -Text "[i] Sending notifications to security (sent: ", $SummaryEscalationEmails.Count, " out of ", $Summary['NotifySecurity'].Values.Count, ")" -Color White, Yellow, White, Yellow, White, Yellow, White 98 | $SummaryEscalationEmails 99 | } else { 100 | Write-Color -Text "[i] Sending notifications to security is ", "disabled!" -Color White, Yellow, DarkRed 101 | } 102 | 103 | } -------------------------------------------------------------------------------- /Private/Send-PasswordUserNotifications.ps1: -------------------------------------------------------------------------------- 1 | function Send-PasswordUserNofifications { 2 | [CmdletBinding()] 3 | param( 4 | [System.Collections.IDictionary] $UserSection, 5 | [System.Collections.IDictionary] $Summary, 6 | [System.Collections.IDictionary] $Logging, 7 | [ScriptBlock] $TemplatePreExpiry, 8 | [string] $TemplatePreExpirySubject, 9 | [scriptBlock] $TemplatePostExpiry, 10 | [string] $TemplatePostExpirySubject, 11 | [System.Collections.IDictionary] $EmailParameters 12 | ) 13 | if ($UserSection.Enable) { 14 | Write-Color -Text "[i] Sending notifications to users " -Color White, Yellow, White, Yellow, White, Yellow, White 15 | $CountUsers = 0 16 | [Array] $SummaryUsersEmails = foreach ($Notify in $Summary['Notify'].Values) { 17 | $CountUsers++ 18 | $User = $Notify.User 19 | $Rule = $Notify.Rule 20 | 21 | # This shouldn't happen, but just in case, to be removed later on, as ProcessManagerOnly is skipping earlier on 22 | if ($Notify.ProcessManagersOnly -eq $true) { 23 | if ($Logging.NotifyOnSkipUserManagerOnly) { 24 | Write-Color -Text "[i]", " Skipping User (Manager Only - $($Rule.Name)) ", $User.DisplayName, " (", $User.UserPrincipalName, ")", " days to expire: ", $User.DaysToExpire -Color Yellow, White, Magenta, White, Magenta, White, White, Blue 25 | } 26 | continue 27 | } 28 | 29 | $EmailSplat = [ordered] @{} 30 | 31 | if ($Notify.User.DaysToExpire -ge 0) { 32 | if ($Notify.Rule.TemplatePreExpiry) { 33 | # User uses template per rule 34 | $EmailSplat.Template = $Notify.Rule.TemplatePreExpiry 35 | } elseif ($TemplatePreExpiry) { 36 | # User uses global template 37 | $EmailSplat.Template = $TemplatePreExpiry 38 | } else { 39 | # User uses built-in template 40 | $EmailSplat.Template = { 41 | 42 | } 43 | } 44 | if ($Notify.Rule.TemplatePreExpirySubject) { 45 | $EmailSplat.Subject = $Notify.Rule.TemplatePreExpirySubject 46 | } elseif ($TemplatePreExpirySubject) { 47 | $EmailSplat.Subject = $TemplatePreExpirySubject 48 | } else { 49 | $EmailSplat.Subject = '[Password] Your password will expire on $DateExpiry ($DaysToExpire days)' 50 | } 51 | } else { 52 | if ($Notify.Rule.TemplatePostExpiry) { 53 | $EmailSplat.Template = $Notify.Rule.TemplatePostExpiry 54 | } elseif ($TemplatePostExpiry) { 55 | $EmailSplat.Template = $TemplatePostExpiry 56 | } else { 57 | $EmailSplat.Template = { 58 | 59 | } 60 | } 61 | if ($Notify.Rule.TemplatePostExpirySubject) { 62 | $EmailSplat.Subject = $Notify.Rule.TemplatePostExpirySubject 63 | } elseif ($TemplatePostExpirySubject) { 64 | $EmailSplat.Subject = $TemplatePostExpirySubject 65 | } else { 66 | $EmailSplat.Subject = '[Password] Your password expired on $DateExpiry ($DaysToExpire days ago)' 67 | } 68 | } 69 | $EmailSplat.User = $Notify.User 70 | $EmailSplat.EmailParameters = $EmailParameters 71 | 72 | $EmailSplat.EmailDateFormat = $Logging.EmailDateFormat 73 | $EmailSplat.EmailDateFormatUTCConversion = $Logging.EmailDateFormatUTCConversion 74 | 75 | if ($UserSection.SendToDefaultEmail -ne $true) { 76 | $EmailSplat.EmailParameters.To = $Notify.User.EmailAddress 77 | } else { 78 | $EmailSplat.EmailParameters.To = $UserSection.DefaultEmail 79 | } 80 | 81 | $Disabled = $null 82 | $DisabledError = $null 83 | if ($null -ne $Rule.DisableDays) { 84 | if ($User.DaysToExpire -in $Rule.DisableDays) { 85 | Write-Color -Text "[i]", " Disabling User (DisableDays - $($Rule.Name)) ", $User.DisplayName, " (", $User.UserPrincipalName, ")", " days to expire: ", $User.DaysToExpire -Color Yellow, White, Magenta, White, Magenta, White, White, Blue 86 | if ($Rule.DisableWhatIf) { 87 | Write-Color -Text "[i]", " Disabling user ", $User.DisplayName, " (", $User.UserPrincipalName, ") would be disabled" -Color Yellow, White, Red 88 | $Disabled = $false 89 | $DisabledError = 'WhatIf' 90 | } else { 91 | if ($User.Enabled) { 92 | try { 93 | Disable-ADAccount -Identity $User.DistinguishedName -Confirm:$false -ErrorAction Stop 94 | $Disabled = $true 95 | $DisabledError = $null 96 | } catch { 97 | $Disabled = $false 98 | $DisabledError = $_.Exception.Message 99 | Write-Color -Text "[e]", " Disabling user ", $User.DisplayName, " (", $User.UserPrincipalName, ") failed because of error: ", $_.Exception.Message -Color Yellow, White, Red 100 | } 101 | } else { 102 | $Disabled = $false 103 | $DisabledError = 'Already disabled' 104 | Write-Color -Text "[i]", " User ", $User.DisplayName, " (", $User.UserPrincipalName, ") is already disabled" -Color Yellow, White, Red 105 | } 106 | } 107 | } 108 | } 109 | 110 | if ($Notify.User.EmailAddress -like "*@*") { 111 | # Regardless if we send email to default email or to user, if user doesn't have email address we shouldn't send an email 112 | $EmailResult = Send-PasswordEmail @EmailSplat 113 | [PSCustomObject] @{ 114 | UserPrincipalName = $EmailSplat.User.UserPrincipalName 115 | SamAccountName = $EmailSplat.User.SamAccountName 116 | Domain = $EmailSplat.User.Domain 117 | Rule = $Notify.Rule.Name 118 | Status = $EmailResult.Status 119 | StatusWhen = Get-Date -Format "yyyy-MM-dd HH:mm:ss" 120 | StatusError = $EmailResult.Error 121 | SentTo = $EmailResult.SentTo 122 | DateExpiry = $EmailSplat.User.DateExpiry 123 | DaysToExpire = $EmailSplat.User.DaysToExpire 124 | PasswordExpired = $EmailSplat.User.PasswordExpired 125 | PasswordNeverExpires = $EmailSplat.User.PasswordNeverExpires 126 | PasswordLastSet = $EmailSplat.User.PasswordLastSet 127 | EmailFrom = $EmailSplat.User.EmailFrom 128 | Disabled = $Disabled 129 | DisabledError = $DisabledError 130 | } 131 | } else { 132 | # Email not sent 133 | $EmailResult = @{ 134 | Status = $false 135 | Error = 'No email address for user' 136 | SentTo = '' 137 | } 138 | [PSCustomObject] @{ 139 | UserPrincipalName = $EmailSplat.User.UserPrincipalName 140 | SamAccountName = $EmailSplat.User.SamAccountName 141 | Domain = $EmailSplat.User.Domain 142 | Rule = $Notify.Rule.Name 143 | Status = $EmailResult.Status 144 | StatusWhen = Get-Date -Format "yyyy-MM-dd HH:mm:ss" 145 | StatusError = $EmailResult.Error 146 | SentTo = $EmailResult.SentTo 147 | DateExpiry = $EmailSplat.User.DateExpiry 148 | DaysToExpire = $EmailSplat.User.DaysToExpire 149 | PasswordExpired = $EmailSplat.User.PasswordExpired 150 | PasswordNeverExpires = $EmailSplat.User.PasswordNeverExpires 151 | PasswordLastSet = $EmailSplat.User.PasswordLastSet 152 | EmailFrom = $EmailSplat.User.EmailFrom 153 | Disabled = $Disabled 154 | DisabledError = $DisabledError 155 | } 156 | } 157 | if ($Logging.NotifyOnUserSend) { 158 | if ($EmailResult.SentTo) { 159 | if ($EmailResult.Error) { 160 | Write-Color -Text "[i]", " Sending notifications to user ", $Notify.User.DisplayName, " (", $Notify.User.EmailAddress, ")", " status: ", $EmailResult.Status, " sent to: ", $EmailResult.SentTo, ", details: ", $EmailResult.Error -Color Yellow, White, Yellow, White, Yellow, White, White, Blue, White, Blue 161 | } else { 162 | Write-Color -Text "[i]", " Sending notifications to user ", $Notify.User.DisplayName, " (", $Notify.User.EmailAddress, ")", " status: ", $EmailResult.Status, " sent to: ", $EmailResult.SentTo -Color Yellow, White, Yellow, White, Yellow, White, White, Blue, White 163 | } 164 | } else { 165 | Write-Color -Text "[i]", " Skipping notifications to user ", $Notify.User.DisplayName, " (", $Notify.User.EmailAddress, ")", " status: ", $EmailResult.Status, " details: ", $EmailResult.Error -Color Yellow, White, Yellow, White, Yellow, White, White, Blue, White, Blue 166 | } 167 | } 168 | if ($UserSection.SendCountMaximum -gt 0) { 169 | if ($UserSection.SendCountMaximum -le $CountUsers) { 170 | Write-Color -Text "[i]", " Send count maximum reached. There may be more accounts that match the rule." -Color Red, DarkRed 171 | break 172 | } 173 | } 174 | } 175 | Write-Color -Text "[i] Sending notifications to users (sent: ", $SummaryUsersEmails.Count, " out of ", $Summary['Notify'].Values.Count, ")" -Color White, Yellow, White, Yellow, White, Yellow, White 176 | $SummaryUsersEmails 177 | } else { 178 | Write-Color -Text "[i] Sending notifications to users is ", "disabled!" -Color White, Yellow, DarkRed 179 | } 180 | 181 | } -------------------------------------------------------------------------------- /Public/Find-PasswordNotification.ps1: -------------------------------------------------------------------------------- 1 | function Find-PasswordNotification { 2 | <# 3 | .SYNOPSIS 4 | Searches thru XML logs created by Password Solution 5 | 6 | .DESCRIPTION 7 | Searches thru XML logs created by Password Solution 8 | 9 | .PARAMETER SearchPath 10 | Path to file where the XML log is located 11 | 12 | .PARAMETER Manager 13 | Search thru manager escalations 14 | 15 | .EXAMPLE 16 | Find-PasswordNotification -SearchPath $PSScriptRoot\Search\SearchLog.xml | Format-Table 17 | 18 | .EXAMPLE 19 | Find-PasswordNotification -SearchPath "$PSScriptRoot\Search\SearchLog_2021-06.xml" -Manager | Format-Table 20 | 21 | .NOTES 22 | General notes 23 | #> 24 | [CmdletBinding()] 25 | param( 26 | [Parameter(Mandatory)][string] $SearchPath, 27 | [switch] $Manager 28 | ) 29 | if ($SearchPath) { 30 | if (Test-Path -LiteralPath $SearchPath) { 31 | try { 32 | $SummarySearch = Import-Clixml -LiteralPath $SearchPath -ErrorAction Stop 33 | #$SummarySearch = Get-Content -LiteralPath $SearchPath -Raw | ConvertFrom-Json 34 | } catch { 35 | Write-Color -Text "[e]", " Couldn't load the file $SearchPath", ". Skipping...", $_.Exception.Message -Color White, Yellow, White, Yellow, White, Yellow, White 36 | } 37 | if ($SummarySearch -and $Manager) { 38 | $SummarySearch.EmailEscalations.Values 39 | } elseif ($SummarySearch -and $Manager -eq $false) { 40 | $SummarySearch.EmailSent.Values 41 | } 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /Public/New-PasswordConfigurationEmail.ps1: -------------------------------------------------------------------------------- 1 | function New-PasswordConfigurationEmail { 2 | [cmdletBinding(DefaultParameterSetName = 'Compatibility', SupportsShouldProcess)] 3 | param( 4 | [Parameter(ParameterSetName = 'SecureString')] 5 | 6 | [Parameter(ParameterSetName = 'oAuth')] 7 | [Parameter(ParameterSetName = 'Compatibility')] 8 | [alias('SmtpServer')][string] $Server, 9 | 10 | [Parameter(ParameterSetName = 'SecureString')] 11 | 12 | [Parameter(ParameterSetName = 'oAuth')] 13 | [Parameter(ParameterSetName = 'Compatibility')] 14 | [int] $Port, 15 | 16 | [Parameter(Mandatory, ParameterSetName = 'SecureString')] 17 | [Parameter(Mandatory, ParameterSetName = 'oAuth')] 18 | [Parameter(Mandatory, ParameterSetName = 'Graph')] 19 | [Parameter(Mandatory, ParameterSetName = 'MgGraphRequest')] 20 | [Parameter(Mandatory, ParameterSetName = 'Compatibility')] 21 | [Parameter(Mandatory, ParameterSetName = 'SendGrid')] 22 | [object] $From, 23 | 24 | [Parameter(ParameterSetName = 'SecureString')] 25 | [Parameter(ParameterSetName = 'oAuth')] 26 | [Parameter(ParameterSetName = 'Graph')] 27 | [Parameter(ParameterSetName = 'MgGraphRequest')] 28 | [Parameter(ParameterSetName = 'Compatibility')] 29 | [Parameter(ParameterSetName = 'SendGrid')] 30 | [string] $ReplyTo, 31 | 32 | 33 | [Parameter(ParameterSetName = 'SecureString')] 34 | [Parameter(ParameterSetName = 'oAuth')] 35 | [Parameter(ParameterSetName = 'Graph')] 36 | [Parameter(ParameterSetName = 'MgGraphRequest')] 37 | [Parameter(ParameterSetName = 'Compatibility')] 38 | [Parameter(ParameterSetName = 'SendGrid')] 39 | [alias('Importance')][ValidateSet('Low', 'Normal', 'High')][string] $Priority, 40 | 41 | [Parameter(ParameterSetName = 'SecureString')] 42 | [Parameter(ParameterSetName = 'oAuth')] 43 | [Parameter(ParameterSetName = 'Compatibility')] 44 | [ValidateSet('None', 'OnSuccess', 'OnFailure', 'Delay', 'Never')][string[]] $DeliveryNotificationOption, 45 | 46 | [Parameter(ParameterSetName = 'SecureString')] 47 | [Parameter(ParameterSetName = 'oAuth')] 48 | [Parameter(ParameterSetName = 'Compatibility')] 49 | [MailKit.Net.Smtp.DeliveryStatusNotificationType] $DeliveryStatusNotificationType, 50 | 51 | [Parameter(ParameterSetName = 'oAuth')] 52 | [Parameter(Mandatory, ParameterSetName = 'Graph')] 53 | [Parameter(ParameterSetName = 'Compatibility')] 54 | [Parameter(Mandatory, ParameterSetName = 'SendGrid')] 55 | [pscredential] $Credential, 56 | 57 | [Parameter(ParameterSetName = 'SecureString')] 58 | [string] $Username, 59 | 60 | [Parameter(ParameterSetName = 'SecureString')] 61 | [string] $Password, 62 | 63 | [Parameter(ParameterSetName = 'SecureString')] 64 | [Parameter(ParameterSetName = 'oAuth')] 65 | [Parameter(ParameterSetName = 'Compatibility')] 66 | [MailKit.Security.SecureSocketOptions] $SecureSocketOptions, 67 | 68 | [Parameter(ParameterSetName = 'SecureString')] 69 | [Parameter(ParameterSetName = 'oAuth')] 70 | [Parameter(ParameterSetName = 'Compatibility')] 71 | [switch] $UseSsl, 72 | 73 | [Parameter(ParameterSetName = 'SecureString')] 74 | [Parameter(ParameterSetName = 'oAuth')] 75 | [Parameter(ParameterSetName = 'Compatibility')] 76 | [switch] $SkipCertificateRevocation, 77 | 78 | [Parameter(ParameterSetName = 'SecureString')] 79 | [Parameter(ParameterSetName = 'oAuth')] 80 | [Parameter(ParameterSetName = 'Compatibility')] 81 | [alias('SkipCertificateValidatation')][switch] $SkipCertificateValidation, 82 | 83 | [Parameter(ParameterSetName = 'SecureString')] 84 | [Parameter(ParameterSetName = 'oAuth')] 85 | [Parameter(ParameterSetName = 'Compatibility')] 86 | [int] $Timeout, 87 | 88 | [Parameter(ParameterSetName = 'oAuth')] 89 | [alias('oAuth')][switch] $oAuth2, 90 | 91 | [Parameter(ParameterSetName = 'Graph')] 92 | [Parameter(ParameterSetName = 'MgGraphRequest')] 93 | [switch] $RequestReadReceipt, 94 | 95 | [Parameter(ParameterSetName = 'Graph')] 96 | [Parameter(ParameterSetName = 'MgGraphRequest')] 97 | [switch] $RequestDeliveryReceipt, 98 | 99 | [Parameter(ParameterSetName = 'Graph')] 100 | [Parameter(ParameterSetName = 'MgGraphRequest')] 101 | [switch] $Graph, 102 | 103 | [Parameter(ParameterSetName = 'MgGraphRequest')] 104 | [switch] $MgGraphRequest, 105 | 106 | [Parameter(ParameterSetName = 'SecureString')] 107 | [switch] $AsSecureString, 108 | 109 | [Parameter(ParameterSetName = 'SendGrid')] 110 | [switch] $SendGrid, 111 | 112 | [Parameter(ParameterSetName = 'SendGrid')] 113 | [switch] $SeparateTo, 114 | 115 | [Parameter(ParameterSetName = 'Graph')] 116 | [Parameter(ParameterSetName = 'MgGraphRequest')] 117 | [switch] $DoNotSaveToSentItems, 118 | 119 | [Parameter(ParameterSetName = 'SecureString')] 120 | [Parameter(ParameterSetName = 'oAuth')] 121 | [Parameter(ParameterSetName = 'Compatibility')] 122 | [string] $LocalDomain 123 | ) 124 | 125 | $Output = [ordered] @{ 126 | Type = 'PasswordConfigurationEmail' 127 | Settings = [ordered] @{ 128 | Server = if ($PSBoundParameters.ContainsKey('Server')) { $Server } else { $null } 129 | Port = if ($PSBoundParameters.ContainsKey('Port')) { $Port } else { $null } 130 | From = if ($PSBoundParameters.ContainsKey('From')) { $From } else { $null } 131 | ReplyTo = if ($PSBoundParameters.ContainsKey('ReplyTo')) { $ReplyTo } else { $null } 132 | Priority = if ($PSBoundParameters.ContainsKey('Priority')) { $Priority } else { $null } 133 | DeliveryNotificationOption = if ($PSBoundParameters.ContainsKey('DeliveryNotificationOption')) { $DeliveryNotificationOption } else { $null } 134 | DeliveryStatusNotificationType = if ($PSBoundParameters.ContainsKey('DeliveryStatusNotificationType')) { $DeliveryStatusNotificationType } else { $null } 135 | Credential = if ($PSBoundParameters.ContainsKey('Credential')) { $Credential } else { $null } 136 | Username = if ($PSBoundParameters.ContainsKey('Username')) { $Username } else { $null } 137 | Password = if ($PSBoundParameters.ContainsKey('Password')) { $Password } else { $null } 138 | SecureSocketOptions = if ($PSBoundParameters.ContainsKey('SecureSocketOptions')) { $SecureSocketOptions } else { $null } 139 | UseSsl = if ($PSBoundParameters.ContainsKey('UseSsl')) { $UseSsl } else { $null } 140 | SkipCertificateRevocation = if ($PSBoundParameters.ContainsKey('SkipCertificateRevocation')) { $SkipCertificateRevocation } else { $null } 141 | SkipCertificateValidation = if ($PSBoundParameters.ContainsKey('SkipCertificateValidatation')) { $SkipCertificateValidation } else { $null } 142 | Timeout = if ($PSBoundParameters.ContainsKey('Timeout')) { $Timeout } else { $null } 143 | oAuth2 = if ($PSBoundParameters.ContainsKey('oAuth2')) { $oAuth2 } else { $null } 144 | RequestReadReceipt = if ($PSBoundParameters.ContainsKey('RequestReadReceipt')) { $RequestReadReceipt } else { $null } 145 | RequestDeliveryReceipt = if ($PSBoundParameters.ContainsKey('RequestDeliveryReceipt')) { $RequestDeliveryReceipt } else { $null } 146 | Graph = if ($PSBoundParameters.ContainsKey('Graph')) { $Graph } else { $null } 147 | MgGraphRequest = if ($PSBoundParameters.ContainsKey('MgGraphRequest')) { $MgGraphRequest } else { $null } 148 | AsSecureString = if ($PSBoundParameters.ContainsKey('AsSecureString')) { $AsSecureString } else { $null } 149 | SendGrid = if ($PSBoundParameters.ContainsKey('SendGrid')) { $SendGrid } else { $null } 150 | SeparateTo = if ($PSBoundParameters.ContainsKey('SeparateTo')) { $SeparateTo } else { $null } 151 | DoNotSaveToSentItems = if ($PSBoundParameters.ContainsKey('DoNotSaveToSentItems')) { $DoNotSaveToSentItems } else { $null } 152 | WhatIf = $WhatIfPreference 153 | } 154 | } 155 | Remove-EmptyValue -Hashtable $Output.Settings 156 | $Output 157 | } -------------------------------------------------------------------------------- /Public/New-PasswordConfigurationEntra.ps1: -------------------------------------------------------------------------------- 1 | function New-PasswordConfigurationEntra { 2 | [CmdletBinding()] 3 | param( 4 | [switch] $Enable 5 | ) 6 | $Output = [ordered] @{ 7 | Type = "PasswordConfigurationEntra" 8 | Settings = [ordered] @{ 9 | Enabled = $Enable.IsPresent 10 | } 11 | } 12 | $Output 13 | } -------------------------------------------------------------------------------- /Public/New-PasswordConfigurationExternalUsers.ps1: -------------------------------------------------------------------------------- 1 | function New-PasswordConfigurationExternalUsers { 2 | <# 3 | .SYNOPSIS 4 | This function caches users from external systems to be used in the password configuration. 5 | 6 | .DESCRIPTION 7 | This function caches users from external systems to be used in the password configuration. 8 | It provides ability to find user by some property and get another property of the user. 9 | 10 | .PARAMETER Users 11 | Parameter description 12 | 13 | .PARAMETER ActiveDirectoryProperty 14 | Property in Active Directory to search for when comparing against SearchProperty. 15 | 16 | .PARAMETER SearchProperty 17 | Property to cache on the user object. 18 | 19 | .PARAMETER EmailProperty 20 | How the email property is called in the user object. 21 | 22 | .PARAMETER Global 23 | Tells the solution to globally overwrite email addresses for all users. 24 | 25 | .PARAMETER Name 26 | Name of the configuration. Visible in HTML reports. 27 | 28 | .EXAMPLE 29 | New-PasswordConfigurationExternalUsers -Users $ExportDataFromHrSystem -SearchProperty '' -EmailProperty '' -ActiveDirectoryProperty 'SamAccountName' 30 | 31 | .NOTES 32 | General notes 33 | #> 34 | [CmdletBinding()] 35 | param( 36 | [parameter(Mandatory)][string] $Name, 37 | [parameter(Mandatory)][Array] $Users, 38 | [parameter(Mandatory)][string] $ActiveDirectoryProperty, 39 | [parameter(Mandatory)][string] $SearchProperty, 40 | [parameter(Mandatory)][string] $EmailProperty, 41 | [switch] $Global 42 | ) 43 | 44 | $CachedUsers = [ordered] @{} 45 | 46 | if ($Users.Count -gt 0 -and $Users[0].$SearchProperty -and $Users[0].$EmailProperty) { 47 | Write-Color -Text '[+] ', "Caching users for '$Name'" -Color Green, White 48 | } else { 49 | Write-Color -Text '[-] ', "Couldn't cache users as either users not provided or email/search property are invalid. Please fix 'New-PasswordConfigurationExternalUsers'" -Color Yellow, White 50 | return 51 | } 52 | try { 53 | foreach ($User in $Users) { 54 | if ($User.$SearchProperty) { 55 | $CachedUsers[$User.$SearchProperty] = $User | Select-Object -Property $EmailProperty 56 | } 57 | } 58 | } catch { 59 | Write-Color -Text '[-] ', "Couldn't cache users. Please fix 'New-PasswordConfigurationExternalUsers'. Error: ", "$($_.Exception.Message)" -Color Yellow, White, Red 60 | return 61 | } 62 | [ordered] @{ 63 | Type = 'ExternalUsers' 64 | ActiveDirectoryProperty = $ActiveDirectoryProperty 65 | SearchProperty = $SearchProperty 66 | EmailProperty = $EmailProperty 67 | Users = $CachedUsers 68 | Global = $Global.IsPresent 69 | Name = $Name 70 | } 71 | } -------------------------------------------------------------------------------- /Public/New-PasswordConfigurationOption.ps1: -------------------------------------------------------------------------------- 1 | function New-PasswordConfigurationOption { 2 | <# 3 | .SYNOPSIS 4 | Provides a way to create a PasswordConfigurationOption object. 5 | 6 | .DESCRIPTION 7 | This function provides a way to create a PasswordConfigurationOption object. 8 | The object is used to store configuration options for the Password Solution module. 9 | 10 | .PARAMETER ShowTime 11 | Show time in the console output. If not provided, time will not be shown. 12 | Time in the log file is always shown. 13 | 14 | .PARAMETER LogFile 15 | File path to the log file. If not provided, there will be no logging to file 16 | 17 | .PARAMETER TimeFormat 18 | Time format used in the logging functionality. 19 | 20 | .PARAMETER LogMaximum 21 | Maximum number of log files to keep. Default is 0 (unlimited). 22 | Once the number of log files exceeds the limit, the oldest log files will be deleted. 23 | 24 | .PARAMETER NotifyOnSkipUserManagerOnly 25 | Provides a way to control output to screen for SkipUserManagerOnly. 26 | 27 | .PARAMETER NotifyOnSecuritySend 28 | Provides a way to control output to screen for SecuritySend. 29 | 30 | .PARAMETER NotifyOnManagerSend 31 | Provides a way to control output to screen for ManagerSend. 32 | 33 | .PARAMETER NotifyOnUserSend 34 | Provides a way to control output to screen for UserSend. 35 | 36 | .PARAMETER NotifyOnUserMatchingRule 37 | Provides a way to control output to screen for UserMatchingRule. 38 | 39 | .PARAMETER NotifyOnUserDaysToExpireNull 40 | Provides a way to control output to screen for UserDaysToExpireNull. 41 | 42 | .PARAMETER NotifyOnUserMatchingRuleForManager 43 | Provides a way to control output to screen for UserMatchingRuleForManager. 44 | 45 | .PARAMETER NotifyOnUserMatchingRuleForManagerButNotCompliant 46 | Provides a way to control output to screen for UserMatchingRuleForManagerButNotCompliant. 47 | 48 | .PARAMETER SearchPath 49 | Path to XML file that will be used for storing search results. 50 | 51 | .PARAMETER EmailDateFormat 52 | Parameter description 53 | 54 | .PARAMETER EmailDateFormatUTCConversion 55 | Parameter description 56 | 57 | .PARAMETER OverwriteEmailProperty 58 | Parameter description 59 | 60 | .PARAMETER OverwriteManagerProperty 61 | Parameter description 62 | 63 | .PARAMETER FilterOrganizationalUnit 64 | Provides a way to filter users by Organizational Unit limiting the scope of the search. 65 | The search is performed using 'like' operator, so you can use wildcards if needed. 66 | 67 | .PARAMETER SearchBase 68 | Provides a way to filter users by Organizational Unit limiting the scope of the search. 69 | The search is passed to Get-ADUser cmdlet. This command should only be used when not using manager functionality. 70 | Otherwise you won't be able to find matching managers for your users. Use FilterOrganizationalUnit instead. 71 | 72 | .EXAMPLE 73 | $Options = @{ 74 | # Logging to file and to screen 75 | ShowTime = $true 76 | LogFile = "$PSScriptRoot\Logs\PasswordSolution_$(($Date).ToString('yyyy-MM-dd_HH_mm_ss')).log" 77 | TimeFormat = "yyyy-MM-dd HH:mm:ss" 78 | LogMaximum = 365 79 | NotifyOnSkipUserManagerOnly = $false 80 | NotifyOnSecuritySend = $true 81 | NotifyOnManagerSend = $true 82 | NotifyOnUserSend = $true 83 | NotifyOnUserMatchingRule = $false 84 | NotifyOnUserDaysToExpireNull = $false 85 | SearchPath = "$PSScriptRoot\Search\SearchLog_$((Get-Date).ToString('yyyy-MM')).xml" 86 | EmailDateFormat = "yyyy-MM-dd" 87 | EmailDateFormatUTCConversion = $true 88 | FilterOrganizationalUnit = @( 89 | "*OU=Accounts,OU=Administration,DC=ad,DC=evotec,DC=xyz" 90 | "*OU=Administration,DC=ad,DC=evotec,DC=xyz" 91 | ) 92 | } 93 | New-PasswordConfigurationOption @Options 94 | 95 | .NOTES 96 | General notes 97 | #> 98 | [CmdletBinding()] 99 | param( 100 | [switch] $ShowTime , #= $true 101 | [string] $LogFile , #= "$PSScriptRoot\Logs\PasswordSolution_$(($Date).ToString('yyyy-MM-dd_HH_mm_ss')).log" 102 | [string] $TimeFormat , #= "yyyy-MM-dd HH:mm:ss" 103 | [int] $LogMaximum , #= 365 104 | [switch] $NotifyOnSkipUserManagerOnly , #= $false 105 | [switch] $NotifyOnSecuritySend , #= $true 106 | [switch] $NotifyOnManagerSend , #= $true 107 | [switch] $NotifyOnUserSend , #= $true 108 | [switch] $NotifyOnUserMatchingRule , #= $true 109 | [switch] $NotifyOnUserDaysToExpireNull , #= $true 110 | [switch] $NotifyOnUserMatchingRuleForManager, 111 | [switch] $NotifyOnUserMatchingRuleForManagerButNotCompliant, 112 | [string] $SearchPath, 113 | [string] $EmailDateFormat, 114 | [switch] $EmailDateFormatUTCConversion, 115 | [string] $OverwriteEmailProperty, 116 | [string] $OverwriteManagerProperty, 117 | [string[]] $FilterOrganizationalUnit, 118 | [string[]] $SearchBase 119 | ) 120 | 121 | $Output = [ordered] @{ 122 | Type = "PasswordConfigurationOption" 123 | Settings = [ordered] @{ 124 | ShowTime = $ShowTime.IsPresent 125 | LogFile = $LogFile 126 | TimeFormat = $TimeFormat 127 | LogMaximum = $LogMaximum 128 | NotifyOnSkipUserManagerOnly = $NotifyOnSkipUserManagerOnly.IsPresent 129 | NotifyOnSecuritySend = $NotifyOnSecuritySend.IsPresent 130 | NotifyOnManagerSend = $NotifyOnManagerSend.IsPresent 131 | NotifyOnUserSend = $NotifyOnUserSend.IsPresent 132 | NotifyOnUserMatchingRule = $NotifyOnUserMatchingRule.IsPresent 133 | NotifyOnUserDaysToExpireNull = $NotifyOnUserDaysToExpireNull.IsPresent 134 | NotifyOnUserMatchingRuleForManager = $NotifyOnUserMatchingRuleForManager.IsPresent 135 | NotifyOnUserMatchingRuleForManagerButNotCompliant = $NotifyOnUserMatchingRuleForManagerButNotCompliant.IsPresent 136 | SearchPath = $SearchPath 137 | # conversion for DateExpiry/PasswordLastSet only 138 | EmailDateFormat = $EmailDateFormat 139 | EmailDateFormatUTCConversion = $EmailDateFormatUTCConversion.IsPresent 140 | # email property conversion (global) 141 | OverwriteEmailProperty = $OverwriteEmailProperty 142 | # manager property conversion (global) 143 | OverwriteManagerProperty = $OverwriteManagerProperty 144 | # filtering 145 | FilterOrganizationalUnit = $FilterOrganizationalUnit 146 | # SearchBase 147 | SearchBase = $SearchBase 148 | } 149 | } 150 | Remove-EmptyValue -Hashtable $Output.Settings 151 | $Output 152 | } -------------------------------------------------------------------------------- /Public/New-PasswordConfigurationReplacement.ps1: -------------------------------------------------------------------------------- 1 | function New-PasswordConfigurationReplacement { 2 | <# 3 | .SYNOPSIS 4 | Password configuration replacement function for replacing properties in the password configuration 5 | 6 | .DESCRIPTION 7 | Password configuration replacement function for replacing properties in the password configuration 8 | This function is used to create a replacement configuration for password properties. 9 | It takes a property name, a type of comparison, a hash table for property replacements, and an optional overwrite property name. 10 | It provides ability to replace specific value on given user object, on given property with different value. 11 | For example user having ExtensionAttribute1 with value 'PL' can be replaced with 'Poland'. 12 | 13 | .PARAMETER PropertyName 14 | Property name to be replaced in the password configuration. 15 | This is the name of the property in the password configuration that will be replaced. 16 | 17 | .PARAMETER Type 18 | Type of comparison to be used for the replacement. 19 | This parameter specifies the type of comparison to be used when replacing the property value. 20 | 21 | .PARAMETER PropertyReplacementHash 22 | Hash table containing the property replacements to be made. 23 | This parameter specifies the hash table that contains the mappings of property values to be replaced. 24 | 25 | .PARAMETER OverwritePropertyName 26 | Name of the property to be overwritten in the password configuration. 27 | This parameter specifies the name of the property that will be overwritten in the password configuration. 28 | This is an optional parameter. 29 | If the property doesn't exists, it will be added. 30 | If the property exists, it will be overwritten regardless of it's value. 31 | 32 | .EXAMPLE 33 | $showPasswordQualitySplat = @{ 34 | FilePath = "$PSScriptRoot\Reporting\PasswordQuality_$(Get-Date -f yyyy-MM-dd_HHmmss).html" 35 | WeakPasswords = "Test1", "Test2", "Test3", 'February2023!#!@ok', $Passwords | ForEach-Object { $_ } 36 | SeparateDuplicateGroups = $true 37 | PassThru = $true 38 | AddWorldMap = $true 39 | LogPath = "$PSScriptRoot\Logs\PasswordQuality_$(Get-Date -f yyyy-MM-dd_HHmmss).log" 40 | Online = $true 41 | LogMaximum = 5 42 | Replacements = New-PasswordConfigurationReplacement -PropertyName 'Country' -Type eq -PropertyReplacementHash @{ 43 | 'PL' = 'Poland' 44 | 'DE' = 'Germany' 45 | 'AT' = 'Austria' 46 | 'IT' = 'Italy' 47 | 'Unknown' = 'Not specified in AD' 48 | } -OverwritePropertyName 'AddMe' 49 | } 50 | 51 | Show-PasswordQuality @showPasswordQualitySplat -Verbose 52 | 53 | .EXAMPLE 54 | $Replacements = @( 55 | New-PasswordConfigurationReplacement -PropertyName 'Country' -Type eq -PropertyReplacementHash @{ 56 | 'PL' = 'Poland' 57 | 'DE' = 'Germany' 58 | 'AT' = 'Austria' 59 | 'IT' = 'Italy' 60 | 'Unknown' = 'Not specified in AD' 61 | } -OverwritePropertyName 'CountryCode' 62 | ) 63 | 64 | $Users = Find-PasswordQuality -Replacements $Replacements 65 | $Users | Format-Table 66 | 67 | .NOTES 68 | General notes 69 | #> 70 | [CmdletBinding()] 71 | param( 72 | [Parameter(Mandatory)][string] $PropertyName, 73 | [ValidateSet('eq')][string] $Type = 'eq', 74 | [Parameter(Mandatory)][System.Collections.IDictionary] $PropertyReplacementHash, 75 | [string] $OverwritePropertyName 76 | ) 77 | if ($PropertyReplacementHash.Count -eq 0) { 78 | Write-Color -Text '[-] ', "Couldn't create replacement configuration as the hash is empty. Please fix 'New-PasswordConfigurationReplacement'" -Color Yellow, White 79 | return 80 | } 81 | 82 | $Output = [ordered] @{ 83 | Type = "PasswordConfigurationReplacement" 84 | Settings = @{ 85 | PropertyName = $PropertyName 86 | Type = $Type 87 | PropertyReplacementHash = $PropertyReplacementHash 88 | OverwritePropertyName = $OverwritePropertyName 89 | } 90 | } 91 | $Output 92 | } -------------------------------------------------------------------------------- /Public/New-PasswordConfigurationReport.ps1: -------------------------------------------------------------------------------- 1 | function New-PasswordConfigurationReport { 2 | <# 3 | .SYNOPSIS 4 | Provides HTML report configuration for Password Notifications in Password Solution. 5 | 6 | .DESCRIPTION 7 | Provides HTML report configuration for Password Notifications in Password Solution. 8 | The New-PasswordConfigurationReport function generates configuration for HTML report. 9 | 10 | .PARAMETER Enable 11 | Specifies whether to enable the report generation. The default value is $false. 12 | 13 | .PARAMETER ShowHTML 14 | Specifies whether to display the report in HTML format right after it's generated in default browser. The default value is $false. 15 | 16 | .PARAMETER Title 17 | Specifies the title of the report. The default value is "Password Solution Summary". 18 | 19 | .PARAMETER Online 20 | Specifies whether to generate the report using CDN for CSS and JS scripts, or use it locally. 21 | It doesn't require internet connectivity during generation. 22 | Makes the final output 3MB smaller. The default value is $false. 23 | 24 | .PARAMETER DisableWarnings 25 | Specifies whether to disable warning messages during report generation. The default value is $false. 26 | 27 | .PARAMETER ShowConfiguration 28 | Specifies whether to display the current Password Solution configuration settings. The default value is $false. 29 | 30 | .PARAMETER ShowAllUsers 31 | Specifies whether to display information about all user accounts. The default value is $false. 32 | 33 | .PARAMETER ShowRules 34 | Specifies whether to display information from the rules. The default value is $false. 35 | 36 | .PARAMETER ShowUsersSent 37 | Specifies whether to display information about users who have received (or not) password expiry notifications. The default value is $false. 38 | 39 | .PARAMETER ShowManagersSent 40 | Specifies whether to display information about managers who have received password expiry notifications. The default value is $false. 41 | 42 | .PARAMETER ShowEscalationSent 43 | Specifies whether to display information about escalation contacts who have received password expiry notifications. The default value is $false. 44 | 45 | .PARAMETER ShowSkippedUsers 46 | Specifies whether to display information about users who were during password expiry notifications because of inability to asses their expiration date. The default value is $false. 47 | 48 | .PARAMETER ShowSkippedLocations 49 | Specifies whether to display information about locations where skipped users are located. The default value is $false. 50 | 51 | .PARAMETER ShowSearchUsers 52 | Specifies whether to display information for searching who got password expiry notifications. The default value is $false. 53 | 54 | .PARAMETER ShowSearchManagers 55 | Specifies whether to display information for searching who got password expiry notifications and for which accounts from managers. The default value is $false. 56 | 57 | .PARAMETER ShowSearchEscalations 58 | Specifies whether to display information for searching who got password escalation notifications and what's the status of that message. The default value is $false. 59 | 60 | .PARAMETER ShowExternalSystemReplacementsUsers 61 | Specifies whether to display information about users who's email address was replaced by an external system. The default value is $false. 62 | 63 | .PARAMETER ShowExternalSystemReplacementsManagers 64 | Specifies whether to display information about managers who's email address was replaced by an external system. The default value is $false. 65 | 66 | .PARAMETER FilePath 67 | Specifies the file path for the report 68 | 69 | .PARAMETER AttachToEmail 70 | Specifies whether to attach the report to an administrative email. The default value is $false. 71 | 72 | .PARAMETER NestedRules 73 | Specifies whether to display nested password rules. 74 | Each rule has it's own tab with output. 75 | Having many rules and all other settings enabled can result in a very long list of tabs that's hard to navigate. 76 | This setting forces separate tab for all rules. 77 | The default value is $false. 78 | 79 | .PARAMETER ExcludeProperties 80 | Specifies an array of properties to exclude from the report. The default value is @('Manager', 'ManagerDN', 'MemberOf'). 81 | Manager, ManagerDN are not really needed in the report as they are already displayed in other form. 82 | MemberOf is not needed as it's not really relevant to the report, and can take a lot of space. 83 | 84 | .OUTPUTS 85 | The function returns an ordered dictionary that contains the report settings. 86 | 87 | .EXAMPLE 88 | New-PasswordConfigurationReport -ShowHTML -Title "Password Configuration Report" -FilePath "C:\Reports\PasswordReport.html" 89 | 90 | .EXAMPLE 91 | $Date = Get-Date 92 | $Report = [ordered] @{ 93 | Enable = $true 94 | ShowHTML = $true 95 | Title = "Password Solution Summary" 96 | Online = $true 97 | DisableWarnings = $true 98 | ShowConfiguration = $true 99 | ShowAllUsers = $true 100 | ShowRules = $true 101 | ShowUsersSent = $true 102 | ShowManagersSent = $true 103 | ShowEscalationSent = $true 104 | ShowSkippedUsers = $true 105 | ShowSkippedLocations = $true 106 | ShowSearchUsers = $true 107 | ShowSearchManagers = $true 108 | ShowSearchEscalations = $true 109 | NestedRules = $false 110 | FilePath = "$PSScriptRoot\Reporting\PasswordSolution_$(($Date).ToString('yyyy-MM-dd_HH_mm_ss')).html" 111 | AttachToEmail = $true 112 | } 113 | New-PasswordConfigurationReport @Report 114 | 115 | #> 116 | [CmdletBinding()] 117 | param( 118 | [switch] $Enable, 119 | [switch] $ShowHTML, 120 | [string] $Title, 121 | [switch] $Online, 122 | [switch] $DisableWarnings, 123 | [switch] $ShowConfiguration, 124 | [switch] $ShowAllUsers, 125 | [switch] $ShowRules, 126 | [switch] $ShowUsersSent, 127 | [switch] $ShowManagersSent, 128 | [switch] $ShowEscalationSent, 129 | [switch] $ShowSkippedUsers, 130 | [switch] $ShowSkippedLocations, 131 | [switch] $ShowSearchUsers, 132 | [switch] $ShowSearchManagers, 133 | [switch] $ShowSearchEscalations , 134 | [string] $FilePath, 135 | [switch] $AttachToEmail, 136 | [switch] $NestedRules, 137 | [switch] $ShowExternalSystemReplacementsUsers, 138 | [switch] $ShowExternalSystemReplacementsManagers, 139 | [string[]] $ExcludeProperties = @('Manager', 'ManagerDN', 'MemberOf') 140 | ) 141 | 142 | $Output = [ordered] @{ 143 | Type = "PasswordConfigurationReport" 144 | Settings = [ordered] @{ 145 | Enable = $Enable.IsPresent 146 | ShowHTML = $ShowHTML.IsPresent 147 | Title = $Title 148 | Online = $Online.IsPresent 149 | DisableWarnings = $DisableWarnings.IsPresent 150 | ShowConfiguration = $ShowConfiguration.IsPresent 151 | ShowAllUsers = $ShowAllUsers.IsPresent 152 | ShowRules = $ShowRules.IsPresent 153 | ShowUsersSent = $ShowUsersSent.IsPresent 154 | ShowManagersSent = $ShowManagersSent.IsPresent 155 | ShowEscalationSent = $ShowEscalationSent.IsPresent 156 | ShowSkippedUsers = $ShowSkippedUsers.IsPresent 157 | ShowSkippedLocations = $ShowSkippedLocations.IsPresent 158 | ShowSearchUsers = $ShowSearchUsers.IsPresent 159 | ShowSearchManagers = $ShowSearchManagers.IsPresent 160 | ShowSearchEscalations = $ShowSearchEscalations.IsPresent 161 | FilePath = $FilePath 162 | AttachToEmail = $AttachToEmail.IsPresent 163 | NestedRules = $NestedRules.IsPresent 164 | ShowExternalSystemReplacementsUsers = $ShowExternalSystemReplacementsUsers.IsPresent 165 | ShowExternalSystemReplacementsManagers = $ShowExternalSystemReplacementsManagers.IsPresent 166 | ExcludeProperties = $ExcludeProperties 167 | } 168 | } 169 | $Output 170 | } -------------------------------------------------------------------------------- /Public/New-PasswordConfigurationRule.ps1: -------------------------------------------------------------------------------- 1 | function New-PasswordConfigurationRule { 2 | <# 3 | .SYNOPSIS 4 | Short description 5 | 6 | .DESCRIPTION 7 | Long description 8 | 9 | .PARAMETER ReminderConfiguration 10 | Parameter description 11 | 12 | .PARAMETER Name 13 | Parameter description 14 | 15 | .PARAMETER Enable 16 | Parameter description 17 | 18 | .PARAMETER IncludeExpiring 19 | Parameter description 20 | 21 | .PARAMETER IncludePasswordNeverExpires 22 | Parameter description 23 | 24 | .PARAMETER PasswordNeverExpiresDays 25 | Parameter description 26 | 27 | .PARAMETER IncludeName 28 | Include user in rule if any of the properties match the value of Name in the properties defined in IncludeNameProperties 29 | 30 | .PARAMETER IncludeNameProperties 31 | Include user in rule if any of the properties match the value as defined in IncludeName 32 | 33 | .PARAMETER ExcludeName 34 | Exclude user from rule if any of the properties match the value of Name in the properties defined in ExcludeNameProperties 35 | 36 | .PARAMETER ExcludeNameProperties 37 | Exclude user from rule if any of the properties match the value as defined in ExcludeName 38 | 39 | .PARAMETER IncludeOU 40 | Include user in rule if user is in any of the OUs defined in the IncludeOU parameter 41 | 42 | .PARAMETER ExcludeOU 43 | Exclude user from rule if users are in any of the OUs defined in the ExcludeOU parameter 44 | 45 | .PARAMETER ExcludeOUFromOtherRules 46 | Exclude users from rule if the user is in any of the OUs defined in the IncludeOU parameter in all other rules above it 47 | 48 | .PARAMETER IncludeGroup 49 | Include user in rule if users are in any of the groups defined in the IncludeGroup parameter 50 | 51 | .PARAMETER ExcludeGroup 52 | Exclude user from rule if users are in any of the groups defined in the ExcludeGroup parameter 53 | 54 | .PARAMETER ReminderDays 55 | Days before expiration to send reminder. If not set and ProcessManagersOnly is not set, the rule will throw an error. 56 | 57 | .PARAMETER ManagerReminder 58 | Parameter description 59 | 60 | .PARAMETER ManagerNotCompliant 61 | Parameter description 62 | 63 | .PARAMETER ManagerNotCompliantDisplayName 64 | Parameter description 65 | 66 | .PARAMETER ManagerNotCompliantEmailAddress 67 | Parameter description 68 | 69 | .PARAMETER ManagerNotCompliantDisabled 70 | Parameter description 71 | 72 | .PARAMETER ManagerNotCompliantMissing 73 | Parameter description 74 | 75 | .PARAMETER ManagerNotCompliantMissingEmail 76 | Parameter description 77 | 78 | .PARAMETER ManagerNotCompliantLastLogonDays 79 | Parameter description 80 | 81 | .PARAMETER SecurityEscalation 82 | Parameter description 83 | 84 | .PARAMETER SecurityEscalationDisplayName 85 | Parameter description 86 | 87 | .PARAMETER SecurityEscalationEmailAddress 88 | Parameter description 89 | 90 | .PARAMETER OverwriteEmailProperty 91 | Overwrite email property for specific rule. This is used to overwrite the email address of the user in the rule. 92 | For example, if the user has an email address in the property 'mail' and you want to use the property 'ExtensionAttribute7' instead, 93 | you can set this parameter to 'ExtensionAttribute7'. The email address will be used for the user in the rule. 94 | 95 | .PARAMETER OverwriteManagerProperty 96 | Overwrite manager property for specific rule. This is used to overwrite the manager of the user in the rule. 97 | For example, if the user has a manager in the property 'manager' and you want to use the property 'ExtensionAttribute8' instead, 98 | you can set this parameter to 'ExtensionAttribute8'. The manager will be used for the user in the rule. 99 | 100 | .PARAMETER OverwriteEmailFromExternalUsers 101 | Allow to overwrite email from external users for specific rule 102 | 103 | .PARAMETER ProcessManagersOnly 104 | This parameters is used to process users, but only managers will be notified. 105 | Sending emails to users within the rule will be skipped completly. 106 | This is useful if users would have email addresses, that would normally trigger an email to them. 107 | 108 | .PARAMETER DisableDays 109 | Define days to disable the user. This is used to disable users that are not already expired. 110 | 111 | .PARAMETER DisableWhatIf 112 | If set, the user will not be disabled, but a message will be shown that the user would be disabled. 113 | 114 | .PARAMETER DisableType 115 | Type of comparison to use for the DisableDays parameter. Default is 'eq'. 116 | Possible values are 'eq', 'in', 'lt', 'gt'. 117 | 'eq' - Days of expiration has to be equal to the value of DisableDays 118 | 'in' - Days of expiration has to be in the value of DisableDays 119 | 'lt' - Days of expiration has to be less than the value of DisableDays 120 | 'gt' - Days of expiration has to be greater than the value of DisableDays 121 | 122 | .EXAMPLE 123 | An example 124 | 125 | .NOTES 126 | General notes 127 | #> 128 | [CmdletBinding()] 129 | param( 130 | [scriptblock] $ReminderConfiguration, 131 | [parameter(Mandatory)][string] $Name, 132 | [switch] $Enable, 133 | [switch] $IncludeExpiring, 134 | [switch] $IncludePasswordNeverExpires, 135 | [nullable[int]]$PasswordNeverExpiresDays, 136 | [string[]] $IncludeNameProperties, 137 | [string[]] $IncludeName, 138 | 139 | [string[]] $ExcludeNameProperties, 140 | [string[]] $ExcludeName, 141 | 142 | [string[]] $IncludeOU, 143 | [switch] $ExcludeOUFromOtherRules, 144 | 145 | [string[]] $ExcludeOU, 146 | [string[]] $IncludeGroup, 147 | [string[]] $ExcludeGroup, 148 | 149 | [alias('ExpirationDays', 'Days')][Array] $ReminderDays, 150 | 151 | [switch] $ManagerReminder, 152 | 153 | [switch] $ManagerNotCompliant, 154 | [string] $ManagerNotCompliantDisplayName, 155 | [string] $ManagerNotCompliantEmailAddress, 156 | 157 | [switch] $ManagerNotCompliantDisabled, 158 | [switch] $ManagerNotCompliantMissing, 159 | [switch]$ManagerNotCompliantMissingEmail, 160 | [nullable[int]] $ManagerNotCompliantLastLogonDays, 161 | 162 | [switch] $SecurityEscalation, 163 | [string] $SecurityEscalationDisplayName, 164 | [string] $SecurityEscalationEmailAddress, 165 | 166 | [string] $OverwriteEmailProperty, 167 | [string] $OverwriteManagerProperty, 168 | 169 | [switch] $ProcessManagersOnly, 170 | 171 | [switch] $OverwriteEmailFromExternalUsers, 172 | 173 | [ValidateSet('eq', 'in', 'lt', 'gt')][string] $DisableType = 'eq', 174 | [Array] $DisableDays, 175 | [switch] $DisableWhatIf 176 | 177 | ) 178 | # Check if the parameters are set correctly 179 | if (-not $ProcessManagersOnly) { 180 | if ($null -eq $ReminderDays) { 181 | $ErrorMessage = "'ReminderDays' is required for rule '$Name', unless 'ProcessManagersOnly' is set. This is to make sure the rule is not skipped completly." 182 | Write-Color -Text "[e]", " Processing rule ", $Name, " failed because of error: ", $ErrorMessage -Color Yellow, White, Red 183 | return [ordered] @{ 184 | Type = 'PasswordConfigurationRule' 185 | Error = $ErrorMessage 186 | } 187 | } 188 | } 189 | # Logic to check if the DisableDays is set and if the DisableType is valid 190 | if ($DisableDays.Count -gt 0) { 191 | if ($DisableType -in 'eq', 'lt', 'gt') { 192 | if ($DisableDays.Count -gt 1) { 193 | $ErrorMessage = "Only one number for 'DisableDays' can be specified for Rule when using comparison types 'eq', 'lt', and 'gt'. Current values are $($DisableDays -join ', ') for '$DisableType'" 194 | Write-Color -Text "[e]", " Processing rule ", $Name, " failed because of error: ", $ErrorMessage -Color Yellow, White, Red 195 | return [ordered] @{ 196 | Type = 'PasswordConfigurationRule' 197 | Error = $ErrorMessage 198 | } 199 | } else { 200 | $DisableDaysToUse = $DisableDays[0] 201 | } 202 | } else { 203 | $DisableDaysToUse = $DisableDays 204 | } 205 | } 206 | 207 | $Output = [ordered] @{ 208 | Name = $Name 209 | Enable = $Enable.IsPresent 210 | IncludeExpiring = $IncludeExpiring.IsPresent 211 | IncludePasswordNeverExpires = $IncludePasswordNeverExpires.IsPresent 212 | Reminders = $ReminderDays 213 | PasswordNeverExpiresDays = $PasswordNeverExpiresDays 214 | IncludeNameProperties = $IncludeNameProperties 215 | IncludeName = $IncludeName 216 | IncludeOU = $IncludeOU 217 | ExcludeOUFromOtherRules = $ExcludeOUFromOtherRules.IsPresent 218 | ExcludeOU = $ExcludeOU 219 | SendToManager = [ordered] @{} 220 | 221 | ProcessManagersOnly = $ProcessManagersOnly.IsPresent 222 | 223 | OverwriteEmailProperty = $OverwriteEmailProperty 224 | # properties to overwrite manager based on different field 225 | OverwriteManagerProperty = $OverwriteManagerProperty 226 | 227 | OverwriteEmailFromExternalUsers = $OverwriteEmailFromExternalUsers.IsPresent 228 | 229 | DisableType = $DisableType 230 | DisableDays = $DisableDaysToUse 231 | DisableWhatIf = $DisableWhatIf.IsPresent 232 | } 233 | $Output.SendToManager['Manager'] = [ordered] @{ 234 | Enable = $false 235 | Reminders = [ordered] @{} 236 | } 237 | $Output.SendToManager['ManagerNotCompliant'] = [ordered] @{ 238 | Enable = $false 239 | Manager = [ordered] @{ 240 | DisplayName = $ManagerNotCompliantDisplayName 241 | EmailAddress = $ManagerNotCompliantEmailAddress 242 | } 243 | Disabled = $ManagerNotCompliantDisabled 244 | Missing = $ManagerNotCompliantMissing 245 | MissingEmail = $ManagerNotCompliantMissingEmail 246 | LastLogon = if ($PSBoundParameters.ContainsKey('ManagerNotCompliantLastLogonDays')) { $true } else { $false } 247 | LastLogonDays = $ManagerNotCompliantLastLogonDays 248 | Reminders = [ordered] @{ } 249 | } 250 | $Output.SendToManager['SecurityEscalation'] = [ordered] @{ 251 | Enable = $false 252 | Manager = [ordered] @{ 253 | DisplayName = $SecurityEscalationDisplayName 254 | EmailAddress = $SecurityEscalationEmailAddress 255 | } 256 | Reminders = [ordered] @{} 257 | } 258 | if ($ManagerReminder) { 259 | $Output.SendToManager['Manager'].Enable = $true 260 | } 261 | if ($ManagerNotCompliant) { 262 | $Output.SendToManager['ManagerNotCompliant'].Enable = $true 263 | } 264 | if ($SecurityEscalation) { 265 | $Output.SendToManager['SecurityEscalation'].Enable = $true 266 | } 267 | if ($ReminderConfiguration) { 268 | try { 269 | $RemindersExecution = & $ReminderConfiguration 270 | } catch { 271 | Write-Color -Text "[e]", " Processing rule ", $Output.Name, " failed because of error: ", $_.Exception.Message -Color Yellow, White, Red 272 | return [ordered] @{ 273 | Type = 'PasswordConfigurationRule' 274 | Error = $_.Exception.Message 275 | } 276 | } 277 | $ManagerRemindersFound = $false 278 | foreach ($Reminder in $RemindersExecution) { 279 | if ($Reminder.Type -eq 'Manager') { 280 | foreach ($ReminderReminders in $Reminder.Reminders) { 281 | $Output.SendToManager['Manager'].Reminders += $ReminderReminders 282 | $ManagerRemindersFound = $true 283 | } 284 | } elseif ($Reminder.Type -eq 'ManagerNotCompliant') { 285 | foreach ($ReminderReminders in $Reminder.Reminders) { 286 | $Output.SendToManager['ManagerNotCompliant'].Reminders += $ReminderReminders 287 | $ManagerRemindersFound = $true 288 | } 289 | } elseif ($Reminder.Type -eq 'Security') { 290 | foreach ($ReminderReminders in $Reminder.Reminders) { 291 | $Output.SendToManager['SecurityEscalation'].Reminders += $ReminderReminders 292 | $ManagerRemindersFound = $true 293 | } 294 | } else { 295 | # Should not happen 296 | throw "Invalid reminder type: $($Reminder.Type)" 297 | } 298 | } 299 | if ($ProcessManagersOnly) { 300 | if (-not $ManagerRemindersFound) { 301 | $ErrorMessage = "At least 1 reminder in 'ReminderConfiguration' is required for rule '$Name' when 'ProcessManagersOnly' is set. This is to make sure the rule is not skipped completly." 302 | Write-Color -Text "[e]", " Processing rule ", $Name, " failed because of error: ", $ErrorMessage -Color Yellow, White, Red 303 | return [ordered] @{ 304 | Type = 'PasswordConfigurationRule' 305 | Error = $ErrorMessage 306 | } 307 | } 308 | } 309 | } 310 | 311 | Remove-EmptyValue -Hashtable $Output -Recursive -Rerun 2 312 | $Configuration = [ordered] @{ 313 | Type = 'PasswordConfigurationRule' 314 | Settings = $Output 315 | } 316 | $Configuration 317 | } -------------------------------------------------------------------------------- /Public/New-PasswordConfigurationRuleReminder.ps1: -------------------------------------------------------------------------------- 1 | function New-PasswordConfigurationRuleReminder { 2 | [CmdletBinding(DefaultParameterSetName = 'Daily')] 3 | param( 4 | [Parameter(Mandatory, ParameterSetName = 'Daily')] 5 | [Parameter(Mandatory, ParameterSetName = 'DayOfWeek')] 6 | [Parameter(Mandatory, ParameterSetName = 'DayOfMonth')] 7 | [ValidateSet('Manager', 'ManagerNotCompliant', 'Security')][string] $Type, 8 | 9 | [Parameter(Mandatory, ParameterSetName = 'Daily')] 10 | [Parameter(Mandatory, ParameterSetName = 'DayOfWeek')] 11 | [Parameter(Mandatory, ParameterSetName = 'DayOfMonth')] 12 | [alias('ConditionDays', 'Days')][Array] $ExpirationDays, 13 | 14 | [Parameter(Mandatory, ParameterSetName = 'DayOfWeek')] 15 | [ValidateSet( 16 | 'Monday', 17 | 'Tuesday', 18 | 'Wednesday', 19 | 'Thursday', 20 | 'Friday', 21 | 'Saturday', 22 | 'Sunday' 23 | )][Array] $DayOfWeek, 24 | 25 | [Parameter(Mandatory, ParameterSetName = 'DayOfMonth')] 26 | [Array] $DayOfMonth, 27 | 28 | [Parameter(ParameterSetName = 'Daily')] 29 | [Parameter(ParameterSetName = 'DayOfWeek')] 30 | [Parameter(ParameterSetName = 'DayOfMonth')] 31 | [ValidateSet('lt', 'gt', 'eq', 'in')][string] $ComparisonType = 'eq' 32 | ) 33 | if ($ComparisonType -in 'eq', 'lt', 'gt') { 34 | if ($ExpirationDays.Count -gt 1) { 35 | throw "Only one number for 'ExpirationDays' can be specified for RuleReminder when using comparison types 'eq', 'lt', and 'gt'. Current values are $($ExpirationDays -join ', ') for '$ComparisonType'" 36 | } else { 37 | $ExpirationDaysToUse = $ExpirationDays[0] 38 | } 39 | } else { 40 | $ExpirationDaysToUse = $ExpirationDays 41 | } 42 | 43 | if ($PSCmdlet.ParameterSetName -eq 'Daily') { 44 | $Reminders = [ordered] @{ 45 | Type = $Type 46 | Reminders = @{ 47 | Default = [ordered] @{ 48 | Enable = $true 49 | } 50 | } 51 | } 52 | } elseif ($PSCmdlet.ParameterSetName -eq 'DayOfWeek') { 53 | $Reminders = [ordered] @{ 54 | Type = $Type 55 | Reminders = @{ 56 | OnDay = [ordered] @{ 57 | Enable = $true 58 | Reminder = $ExpirationDaysToUse 59 | ComparisonType = $ComparisonType 60 | Days = $DayOfWeek 61 | } 62 | } 63 | } 64 | } elseif ($PSCmdlet.ParameterSetName -eq 'DayOfMonth') { 65 | $Reminders = [ordered] @{ 66 | Type = $Type 67 | Reminders = @{ 68 | OnDayOfMonth = [ordered] @{ 69 | Enable = $true 70 | Reminder = $ExpirationDaysToUse 71 | ComparisonType = $ComparisonType 72 | Days = $DayOfMonth 73 | } 74 | } 75 | } 76 | } 77 | $Reminders 78 | } -------------------------------------------------------------------------------- /Public/New-PasswordConfigurationTemplate.ps1: -------------------------------------------------------------------------------- 1 | function New-PasswordConfigurationTemplate { 2 | [CmdletBinding()] 3 | param( 4 | [parameter(Mandatory)][ScriptBlock] $Template, 5 | [parameter(Mandatory)][string] $Subject, 6 | [parameter(Mandatory)][ValidateSet('PreExpiry', 'PostExpiry', 'Manager', 'ManagerNotCompliant', 'Security', 'Admin')] $Type 7 | ) 8 | 9 | $Output = [ordered] @{ 10 | Type = "PasswordConfigurationTemplate$Type" 11 | Settings = [ordered] @{ 12 | Template = $Template 13 | Subject = $Subject 14 | } 15 | } 16 | $Output 17 | } -------------------------------------------------------------------------------- /Public/New-PasswordConfigurationType.ps1: -------------------------------------------------------------------------------- 1 | function New-PasswordConfigurationType { 2 | <# 3 | .SYNOPSIS 4 | Configures behavior of password notification emails for different types of users. 5 | 6 | .DESCRIPTION 7 | Configures behavior of password notification emails for different types of users. 8 | It is used to define how the notification emails should be sent to different types of users. 9 | It supports User, Manager, Security, and Admin types. 10 | Main function is to prevent sending emails to real users during testing phase. 11 | 12 | .PARAMETER Type 13 | Type of the configuration. Possible values are User, Manager, Security, and Admin. 14 | 15 | .PARAMETER Enable 16 | Enable sending emails for the specified type. 17 | 18 | .PARAMETER SendCountMaximum 19 | Maximum number of emails that can be sent for the specified type. This is used to prevent sending emails to a large number of users during testing phase. 20 | 21 | .PARAMETER DefaultEmail 22 | Default email address to which the emails should be sent. This is used to prevent sending emails to real users during testing phase. 23 | All emails will be sent to this email address for the specified type, with the exception of Admin type. 24 | Admin type will send emails to the specified email address as is. 25 | 26 | .PARAMETER AttachCSV 27 | Attach a CSV file with the list of users to the email. 28 | This is used to provide additional information about the users to the recipient. 29 | This is only used for Security type. 30 | 31 | .PARAMETER DisplayName 32 | Display name of the Admin user. This is used to provide a custom display name for the Admin user. 33 | If not specified, the default display name is "Administrators". 34 | 35 | .EXAMPLE 36 | New-PasswordConfigurationType -Type User -Enable -SendCountMaximum 10 -DefaultEmail 'przemyslaw.klys+testgithub1@test.pl' 37 | 38 | .EXAMPLE 39 | New-PasswordConfigurationType -Type Manager -Enable -SendCountMaximum 10 -DefaultEmail 'przemyslaw.klys+testgithub2@test.pl' 40 | 41 | .EXAMPLE 42 | New-PasswordConfigurationType -Type Security -Enable -SendCountMaximum 1 -DefaultEmail 'przemyslaw.klys+testgithub3@test.pl' -AttachCSV 43 | 44 | .EXAMPLE 45 | New-PasswordConfigurationType -Type Admin -Enable -EmailAddress 'przemyslaw.klys+testgithub3@test.pl' -DisplayName 'Administrators' 46 | 47 | .NOTES 48 | General notes 49 | #> 50 | [CmdletBinding()] 51 | param( 52 | [Parameter(Mandatory)][ValidateSet('User', 'Manager', 'Security', 'Admin')][string] $Type, 53 | [switch] $Enable, 54 | [int] $SendCountMaximum, 55 | [Alias('EmailAddress')][string] $DefaultEmail, 56 | [switch] $AttachCSV, 57 | [string] $DisplayName 58 | ) 59 | 60 | $Output = [ordered] @{ 61 | Type = "PasswordConfigurationType$Type" 62 | Settings = @{ 63 | Enable = $Enable.IsPresent 64 | SendCountMaximum = $SendCountMaximum 65 | SendToDefaultEmail = if ($DefaultEmail) { $true } else { $false } 66 | DefaultEmail = $DefaultEmail 67 | OverwriteEmailProperty = $OverwriteEmailProperty 68 | AttachCSV = $AttachCSV.IsPresent 69 | } 70 | } 71 | 72 | if ($Type -eq "Admin") { 73 | $Output.Settings.Manager = @{ 74 | DisplayName = if (-not $DisplayName) { "Administrators" } else { $DisplayName } 75 | EmailAddress = $DefaultEmail 76 | } 77 | } 78 | $Output 79 | } -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | 

2 | 3 | 4 | 5 |

6 | 7 |

8 | 9 | 10 | 11 | 12 |

13 | 14 |

15 | 16 | 17 | 18 |

19 | 20 | 21 | # PasswordSolution 22 | 23 | **PasswordSolution** is a PowerShell module that provides Password Expiry notifications to users, managers, security and administrators. It's very configurable and was designed for enterprise use. 24 | 25 | ## Features 26 | 27 | - Find all users with passwords that are expiring and send notifications including service accounts, admin accounts and accounts that never expire 28 | - Find and asses password quality of users in an Active Directory forest. 29 | 30 | ## Support This Project 31 | 32 | If you find this project helpful, please consider supporting its development. 33 | Your sponsorship will help the maintainers dedicate more time to maintenance and new feature development for everyone. 34 | 35 | It takes a lot of time and effort to create and maintain this project. 36 | By becoming a sponsor, you can help ensure that it stays free and accessible to everyone who needs it. 37 | 38 | To become a sponsor, you can choose from the following options: 39 | 40 | - [Become a sponsor via GitHub Sponsors :heart:](https://github.com/sponsors/PrzemyslawKlys) 41 | - [Become a sponsor via PayPal :heart:](https://paypal.me/PrzemyslawKlys) 42 | 43 | Your sponsorship is completely optional and not required for using this project. 44 | We want this project to remain open-source and available for anyone to use for free, 45 | regardless of whether they choose to sponsor it or not. 46 | 47 | If you work for a company that uses our .NET libraries or PowerShell Modules, 48 | please consider asking your manager or marketing team if your company would be interested in supporting this project. 49 | Your company's support can help us continue to maintain and improve this project for the benefit of everyone. 50 | 51 | Thank you for considering supporting this project! 52 | 53 | ## Installing 54 | 55 | Everyone can install this module from **PowerShellGallery** hosted by Microsoft. It's recommended way to work with the module. 56 | Version on **PowershellGallery** is optimized for speed and signed. Using code from **GitHub** is **recommended for development**. 57 | 58 | ```powershell 59 | Install-Module -Name PasswordSolution -AllowClobber -Force -Verbose 60 | ``` 61 | 62 | If you want to use Password Quality checks you need to install DSInternals manually. 63 | Due to sensitive nature of the module it's not included in the package. 64 | 65 | ```powershell 66 | Install-Module DSInternals -Force -Verbose 67 | ``` 68 | 69 | Force and AllowClobber aren't necessary, but they do skip errors in case some appear, and they do update module if newer version is available. 70 | 71 | ## Updating 72 | 73 | ```powershell 74 | Update-Module -Name PasswordSolution 75 | ``` 76 | 77 | That's it. Whenever there's a new version, you run the command, and you can enjoy it. Remember that you may need to close, reopen PowerShell session if you have already used module before updating it. 78 | 79 | **The essential thing** is if something works for you on production, keep using it till you test the new version on a test computer. I do changes that may not be big, but big enough that auto-update may break your code. For example, small rename to a parameter and your code stops working! Be responsible! 80 | 81 | ## Usage - Password Quality 82 | 83 | Password Quality report uses [DSInternals](https://github.com/MichaelGrafnetter/DSInternals) module to scan Active Directory for passwords and then assesses them. 84 | It's a very powerful tool that can be used to find weak passwords in your environment. 85 | It's also a great tool to find out if your password policy is working as expected. 86 | PasswordSolution wraps around DSInternals to make it easier to use and to provide a nice HTML report. 87 | 88 | ```powershell 89 | Show-PasswordQuality -FilePath C:\Temp\PasswordQuality.html -Online -WeakPasswords "Test1", "Test2", "Test3" -Verbose -SeparateDuplicateGroups -AddWorldMap -PassThru 90 | ``` 91 | 92 | or 93 | 94 | ```powershell 95 | $showPasswordQualitySplat = @{ 96 | FilePath = "$PSScriptRoot\Reporting\PasswordQuality_$(Get-Date -f yyyy-MM-dd_HHmmss).html" 97 | WeakPasswords = "Test1", "Test2", "Test3", 'February2023!#!@ok', $Passwords | ForEach-Object { $_ } 98 | SeparateDuplicateGroups = $true 99 | PassThru = $true 100 | AddWorldMap = $true 101 | LogPath = "$PSScriptRoot\Logs\PasswordQuality_$(Get-Date -f yyyy-MM-dd_HHmmss).log" 102 | Online = $true 103 | LogMaximum = 5 104 | } 105 | 106 | Show-PasswordQuality @showPasswordQualitySplat -Verbose 107 | ``` 108 | 109 | And here's what you get 110 | 111 | ![PasswordQuality](https://raw.githubusercontent.com/EvotecIT/PasswordSolution/master/Docs/Images/PasswordQuality1.png) 112 | 113 | ![PasswordQuality](https://raw.githubusercontent.com/EvotecIT/PasswordSolution/master/Docs/Images/PasswordQuality2.png) 114 | 115 | ![PasswordQuality](https://raw.githubusercontent.com/EvotecIT/PasswordSolution/master/Docs/Images/PasswordQuality3.png) 116 | 117 | ![PasswordQuality](https://raw.githubusercontent.com/EvotecIT/PasswordSolution/master/Docs/Images/PasswordQuality4.png) 118 | 119 | ![PasswordQuality](https://raw.githubusercontent.com/EvotecIT/PasswordSolution/master/Docs/Images/PasswordQuality5.png) 120 | 121 | ![PasswordQuality](https://raw.githubusercontent.com/EvotecIT/PasswordSolution/master/Docs/Images/PasswordQuality6.png) --------------------------------------------------------------------------------