Combining Powershell script to call function and get the AD attributes value - powershell

I need to use a PowerShell function to format the phone number like below:
Function Format-TelephoneNumber
{
Param (
[Parameter(ValueFromPipeline = $true, Position = 0)]
[Alias('Number')]
[string]$TelephoneNumber,
[Parameter(Position = 1)]
[string]$DefaultCountryCode = '+44'
)
Process
{
$formattedNumber = $TelephoneNumber -replace '[\x09 ]'
If ($formattedNumber -match '\A(?<CountryCode>\+[1-9]\d|0)(?<Number>\d*)\Z')
{
If ($Matches['CountryCode'] -eq '0')
{
$countryCode = $defaultCountryCode
}
Else
{
$countryCode = $Matches['CountryCode']
}
$formattedNumber = $countryCode + ' '
$formattedNumber += -join $Matches['Number'][0 .. 2] + ' '
$formattedNumber += -join $Matches['Number'][3 .. 5] + ' '
$formattedNumber += -join $Matches['Number'][6 .. 8]
$formattedNumber
}
Else
{
Write-Error "Unable to parse the string '$($number)' as telephone number!"
}
}
}
The below script is for retrieving the value of Phone Number from AD Attribute:
$sysInfo = New-Object -ComObject 'ADSystemInfo'
$userDN = $sysInfo.GetType().InvokeMember('UserName', 'GetProperty', $null, $sysInfo, $null)
$adUser = [ADSI]"LDAP://$($userDN)"
[void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($sysInfo)
Write-Host $adUser.mobile.ToString() -ForegroundColor Green
How can I call the script?
I have tried below but failed:
Write-Host "This is raw from AD: $($adUser.mobile.ToString())" -ForegroundColor Yellow
$Formatted = Format-TelephoneNumber -TelephoneNumber $adUser.mobile.ToString()
Write-Host "This is processed using Function: " "$($Formatted)" -ForegroundColor Green

Personally, I'd use a different Format-TelephoneNumber function because as James C commented, your function may truncate last digit(s) from the number.
Below is my attempt:
function Format-TelephoneNumber {
Param(
[Parameter(ValueFromPipeline = $true, Position = 0)]
[Alias('Number')]
[string]$TelephoneNumber,
[Parameter(Position = 1)]
[string]$DefaultCountryCode = '+44'
)
Process {
# replace all hyphens and other possible joining characters with space and trim the result
$number = ($TelephoneNumber -replace '[._~-]', ' ').Trim()
# test if the number starts with a country code
if ($number -match '^(\+\d+)\s') {
$countryCode = $Matches[1]
$number = $number.Substring($countryCode.Length).Trim()
}
else {
$countryCode = $DefaultCountryCode
}
# remove leading zero and any non-digits
$number = $number -replace '^0|\D', ''
if ($number.Length -lt 9) {
Write-Warning "Unable to parse the string '$($TelephoneNumber)' as telephone number!"
}
else {
$parts = #($countryCode)
# split the remaining string in to 3-character parts (+ possible remainder)
$parts += $number -split '(\d{3})' | Where-Object { $_ }
return $parts -join ' '
}
}
}
Why not use the Get-ADUser cmdlet to find the mobile property? Something like:
Import-Module ActiveDirectory
# return the mobile phone number for a user as string or nothing if not found
# $userID is either the users distinguished name, the GUID, the user SID, or the SamAccountName.
$mobile = Get-ADUser -Identity $userID -Properties MobilePhone | Select-Object -ExpandProperty MobilePhone
Note: MobilePhone is the PowerShell or GUI name for the mobile attribute, but you may use either.
Then, if you have this mobile number as string format it using the Format-TelephoneNumber function:
if ($mobile) {
Write-Host "This is raw from AD: $mobile" -ForegroundColor Yellow
$formatted = Format-TelephoneNumber -TelephoneNumber $mobile
Write-Host "This is formatted: $formatted" -ForegroundColor Green
}
Hope that answers your question

Related

How to remove Disabled account in Windows PowerShell Script

I have PowerShell script which access the AD users and insert into the SharePoint list.
Now the problem is some of the accounts are disabled or not active. I am using the following PowerShell script in the windows task scheduler which do the job in an interval. Can anyone Help me to figure out What I suppose to do in this script to filter only active accounts?
#if not already added
if ((Get-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue) -eq $null) {
Add-PSSnapin "Microsoft.SharePoint.PowerShell"
}
$site = new-object Microsoft.SharePoint.SPSite("https://portal.company.gov.sa/");
$ServiceContext = [Microsoft.SharePoint.SPServiceContext]::GetContext($site);
#Get UserProfileManager from the My Site Host Site context
$ProfileManager = new-object Microsoft.Office.Server.UserProfiles.UserProfileManager($ServiceContext)
$AllProfiles = $ProfileManager.GetEnumerator()
# Open SharePoint List
$spWeb = Get-SPWeb "https://my.gac.gov.sa/"
$spData = $spWeb.GetList("Lists/EmployeesDirectory/")
$spDepartments = $spWeb.GetList("Lists/Departments/")
$total=0;
$withErros=0;
foreach($profile in $AllProfiles)
{
try
{
$DisplayName = $profile.DisplayName
$WorkEmail = $profile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::WorkEmail]
$AccountName = $profile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::AccountName]
$Department = $profile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::Department]
$Position = $profile.JobTitle
$LastName = $profile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::LastName]
$FirstName = $profile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::FirstName]
$FullName= "$FirstName $LastName"
$PreferredName = $profile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::PreferredName]
$WorkPhone =$profile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::WorkPhone]
$Manager = $profile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::Manager]
$JobTitleArabic=$profile["JobTitleArabic"];
if($Department -ine '' -and $Manager -ine ''){
$total++;
$departmnetItem = $spDepartments.Items | Where {$_["FF_TitleEn"] -eq $Department}
# Add properties to this list item
$user=$spWeb.EnsureUser($AccountName);
write-host $DisplayName "|" $AccountName "|" $Department "|" $Position "|" $PreferredName "|" $WorkPhone "|" $Manager ;
if($user.ID -gt 0)
{
#Query to filter List Items which contains user account
$SPQuery = new-object Microsoft.SharePoint.SPQuery
$Query = "<Where><Eq><FieldRef Name='FF_Emlpoyee' LookupId='TRUE'/><Value Type='User'>$($user.ID)</Value></Eq></Where>"
$SPQuery.Query=$Query
#Filter List Items by Query
$ListItems = $spData.GetItems($SPQuery)
if($ListItems.Count -gt 0)
{
$newItem=$ListItems[0];
}
else
{
#Create a new item
$newItem = $spData.Items.Add()
}
$newItem["FF_Emlpoyee"] = $user.ID;
# $newItem["FF_UserID"] = $user.ID;
$newItem["Title"] = $PreferredName
if($WorkPhone -ine '')
{
$newItem["FF_ExtensionNumber"] = $WorkPhone
}
try
{
if($Manager -ine $null)
{
$userManager=$spWeb.EnsureUser($Manager);
$newItem["FF_Manager"] = $userManager.ID
}
}
catch
{
write-host -ForegroundColor Red "Manager Not Found fro : " $user
}
$newItem["FF_Position"] = $Position
IF($JobTitleArabic -ine '')
{
$newItem["FF_PositionAr"] = $JobTitleArabic
}
$newItem["FF_FullNameAr"] = $FullName
$newItem["FF_Department"] = $departmnetItem.ID
$newItem.Update()
Write-Host "---------------------------------";
}
$user=$null
}
}
catch
{
write-host -ForegroundColor Red $_.Exception
$withErros+=1
}
}
Write-Host "Total: " $total;
Write-Host "withErros: " $withErros

PowerShell "New-ADUSer" password complexity error

I will create 100 random AD users via script. But , I am getting the following error message for some random users.
New-ADUser : The password does not meet the length, complexity, or history requirement of the domain.
Get-ADDefaultDomainPasswordPolicy
output :
ComplexityEnabled : True
DistinguishedName : DC=contoso,DC=local
LockoutDuration : 00:00:00
LockoutObservationWindow : 69.10:39:00
LockoutThreshold : 5
MaxPasswordAge : 90.00:00:00
MinPasswordAge : 3.00:00:00
MinPasswordLength : 8
objectClass : {domainDNS}
objectGuid : 346664da-c908-470e-9fc3-5487983c92ae
PasswordHistoryCount : 12
ReversibleEncryptionEnabled : False
Here is my script :
$UserList = Import-CSV -Path C:\temp\CreateUsers.csv
$targetOU = 'OU=Test,DC=contoso,DC=local'
$upnDomain = 'contoso.local'
$Path = "C:\temp\output.csv"
foreach ($Person in $UserList) {
#If username does not exist
if(-not(Get-ADUser -Filter "SamAccountName -eq '$($Person.Sam)'")) {
$PassWord = -join ([char[]]"!##$%^&*0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz" |Get-Random -Count 10)
$useritems = #{
GivenName = $Person.Firstname
Surname = $Person.LastName
AccountPassword = ConvertTo-SecureString -String $Password -AsPlainText -force
ChangePasswordAtLogon = $false
Enabled = $true
DisplayName = "$($Person.Firstname) $($Person.Lastname)"
Name = "$($Person.Firstname) $($Person.Lastname)"
SamAccountName = $Person.Sam
UserPrincipalName = "$($Person.Sam)#$upnDomain"
}
New-ADUser #useritems -Path $targetOU
Add-Content -Path $Path -Value "Username: $($Person.Sam) Password: $Password"
}
}
To avoid creating passwords that do not comply to the domains password complexity rules, you could use this helper function:
function Test-DomainPassword {
[CmdletBinding()]
Param (
[Parameter(Mandatory = $true, Position = 0)]
[string]$Password,
[Parameter(Mandatory = $false)]
[string]$AccountSamAccountName = $null,
[Parameter(Mandatory = $false)]
[string]$AccountDisplayName = $null
)
# [Microsoft.ActiveDirectory.Management.ADEntity]
$PasswordPolicy = Get-ADDefaultDomainPasswordPolicy -ErrorAction SilentlyContinue
If ($Password.Length -lt $PasswordPolicy.MinPasswordLength) {
Write-Verbose "Password '$Password' is too short. Minimal length is $($PasswordPolicy.MinPasswordLength)"
return $false
}
if (![string]::IsNullOrWhiteSpace($AccountSamAccountName) -and $Password -match $AccountSamAccountName) {
Write-Verbose "The password '$Password' includes the users SamAccountName"
return $false
}
if (![string]::IsNullOrWhiteSpace($AccountDisplayName)) {
# if ANY PART of the display name that is split by the characters below, the password should fail the complexity rules.
$tokens = $AccountDisplayName.Split(",.-,_ #`t")
foreach ($token in $tokens) {
if (($token) -and ($Password -match "$token")) {
Write-Verbose "The password '$Password' includes (part of) the users DisplayName"
return $false
}
}
}
if ($PasswordPolicy.ComplexityEnabled) {
# check for presence of
# - Uppercase: A through Z, with diacritic marks, Greek and Cyrillic characters
if ($Password -cnotmatch "[A-Z\p{Lu}\s]") {
Write-Verbose "The password '$Password' is missing Uppercase characters"
return $false
}
# - Lowercase: a through z, sharp-s, with diacritic marks, Greek and Cyrillic characters
if ($Password -cnotmatch "[a-z\p{Ll}\s]") {
Write-Verbose "The password '$Password' is missing Lowercase characters"
return $false
}
# - Base 10 digits (0 through 9)
if ($Password -notmatch "[\d]") {
Write-Verbose "The password '$Password' is missing digits (0-9)"
return $false
}
# - Nonalphanumeric characters: ~!##$%^&*_-+=`|\(){}[]:;”‘<>,.?/
if ($Password -notmatch "[^\w]") {
Write-Verbose "The password '$Password' is missing Nonalphanumeric characters: ~!##$%^&*_-+=`|\(){}[]:;`"'<>,.?/"
return $false
}
}
return $true
}
and use that in a loop:
do {
$PassWord = -join ([char[]]"!##$%^&*0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz" | Get-Random -Count 10)
} until (Test-DomainPassword -Password $PassWord)
If you add the -Verbose switch to the function call, you will see the reason why a password would fail.
Of course, this cannot test if a password meets the domains history requirement, that remains a trial and error thing..
Or you could re-use the code from Test-DomainPassword and create a helper function that returns a valid password like this:
function New-DomainPassword {
[CmdletBinding()]
Param (
[Parameter(Mandatory = $false)]
[int]$Length = 10,
[Parameter(Mandatory = $false)]
[string]$SamAccountName = $null,
[Parameter(Mandatory = $false)]
[string]$DisplayName = $null
)
# [Microsoft.ActiveDirectory.Management.ADEntity]
$passwordPolicy = Get-ADDefaultDomainPasswordPolicy -ErrorAction SilentlyContinue
$passwordLength = [math]::Max($Length, $passwordPolicy.MinPasswordLength)
$count = 1
while ($true) {
Write-Verbose ("Generating valid password attempt: {0}" -f $count++)
$password = -join ([char[]]"!##$%^&*0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz" | Get-Random -Count $passwordLength)
if (![string]::IsNullOrWhiteSpace($SamAccountName) -and $password -match $SamAccountName) {
continue # bad password, skip and try another one
}
if (![string]::IsNullOrWhiteSpace($DisplayName)) {
# if ANY PART of the display name that is split by the characters below, the password should fail the complexity rules.
$tokens = $DisplayName.Split(",.-,_ #`t")
$bad = foreach ($token in $tokens) {
if (($token) -and ($password -match $token)) { $true; break }
}
if ($bad) { continue } # bad password, skip and try another one
}
if ($passwordPolicy.ComplexityEnabled) {
# check for presence of
# - Uppercase: A through Z, with diacritic marks, Greek and Cyrillic characters
if ($password -cnotmatch "[A-Z\p{Lu}\s]") {
continue # bad password, skip and try another one
}
# - Lowercase: a through z, sharp-s, with diacritic marks, Greek and Cyrillic characters
if ($password -cnotmatch "[a-z\p{Ll}\s]") {
continue # bad password, skip and try another one
}
# - Base 10 digits (0 through 9)
if ($password -notmatch "[\d]") {
continue # bad password, skip and try another one
}
# - Nonalphanumeric characters: ~!##$%^&*_-+=`|\(){}[]:;”‘<>,.?/
if ($password -notmatch "[^\w]") {
continue # bad password, skip and try another one
}
}
# apparently all tests succeeded, so break out of the while loop
break
}
# return the new password
$password
}
and use it like this:
$UserList = Import-CSV -Path C:\temp\CreateUsers.csv
$targetOU = 'OU=Test,DC=contoso,DC=local'
$upnDomain = 'contoso.local'
$Path = "C:\temp\output.csv"
foreach ($Person in $UserList) {
#If username does not exist
if(-not(Get-ADUser -Filter "SamAccountName -eq '$($Person.Sam)'")) {
$accountName = $Person.Sam
$displayName = "$($Person.Firstname) $($Person.Lastname)"
$Password = New-DomainPassword -Length 10 -SamAccountName $accountName -DisplayName $displayName
$useritems = #{
GivenName = $Person.Firstname
Surname = $Person.LastName
AccountPassword = ConvertTo-SecureString -String $Password -AsPlainText -force
ChangePasswordAtLogon = $false
Enabled = $true
DisplayName = $displayName
Name = "$($Person.Firstname) $($Person.Lastname)"
SamAccountName = $accountName
UserPrincipalName = "$($Person.Sam)#$upnDomain"
Path = $targetOU
}
New-ADUser #useritems
Add-Content -Path $Path -Value "Username: $accountName Password: $Password"
}
}
Your random password generator is very poor and you do not guarantee that it meets your requirements. There is a .NET function, that you can use to generate random passwords: .GeneratePassword(Int32, Int32).
The first argument specifies the length of the password and the second argument specifies the minimum number of special characters.
You can use the following PowerShell function to generate a random password according to your needs:
function Get-RandomPassword {
param($Length = 8) # Default length of 8, if not provided
# Generate a random password with at least one special character
$password = [System.Web.Security.Membership]::GeneratePassword($Length, 1)
# Check password complexity
if ($password -cnotmatch '[A-Z]' -or $password -cnotmatch '[a-z]' -or $password -cnotmatch '\d') {
# Recursively generate a new password if the complexity requirements are not met
return Get-RandomPassword -Length $Length
} else {
# Return the password, if the complexity requirements are met
return $password
}
}
Example usage:
PS C:\> Get-RandomPassword -Length 10
A8$JfE1e%i
The function generates a random password of the provided length and with at least one special character. It then checks if all other (your individual) requirements are met. If not, the function will call itself recursively, until a password is found, that meets all complexity criteria.
To be honest, it's not the best design, as it might run forever in theory, but you will rather win a lottery than seeing this function running forever ;-)

Function not outputting value?

I'm working on a PowerShell script for some work use, and am having trouble getting a function it's data directly to a variable. Below is the function and my test:
function validateInput {
Param(
[string]$Text = 'Put text here'
)
do {
try {
$numOk = $true
$GetMyANumber = Read-Host "$text"
if ($GetMyANumber -gt 3){
$numOK = $false
}
} catch {
$numOK = $false
}
if ($numOK -ne $true) {
cls
Write-Host "Please enter a valid number" -Foreground Red
}
} until (($GetMyANumber -ge 0 -and $GetMyANumber -le 3) -and $numOK)
}
$firstName = validateInput($firstNamePrompt)
$lastName = validateInput ($lastNamePrompt)
Write-Host "First name length is $firstname"
Write-Host "Last name length is $lastname"
My understanding is that the in the last few lines, the function SHOULD assign it's output to the variables $firstName and $lastName, but I output that, they are blank. I'm sure I'm missing something obvious here, but can anyone let me know what I'm screwing up?

powershell highlight the blank space found

I have the following script which finds a blank space in a csv file by going line by line
$yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Retain blank space."
$no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Delete blank space."
$n = #()
$f = Get-Content C:\MyPath\*.csv
foreach($item in $f) {
if($item -like "* *"){
$res = $host.ui.PromptForChoice("Title", "want to keep the blank on this line? `n $item", [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no), 0)
switch ($res)
{
0 {$n+=$item}
1 {$n+=$item -replace ' '}
}
} else {
$n+=$item -replace ' '
}
}
$n | Set-Content C:\MyPath\*.csv
The question is: When there is a space found, how can I highlight where it is on a line or put a color there, anything that would ease the process of finding it?
EDIT: don't want to alter the file or the text, this should be done only shown in the console for PowerShell or in the window popup for ISE.
A basic code sample for the method described in the comments using Read-Host for user input and changing the background color with write-host would look like this:
$str= "test abc1 abc2 test3"
$index = $str.IndexOf(" ")
while ($index -gt -1) {
write-host $str.Substring(0,$index) -NoNewline
write-host "_" -foreground "magenta" -NoNewline
$str = $str.Substring( $index + 1, $str.length - $index - 1);
$index = $str.IndexOf(" ")
}
write-host $str.Substring( $index + 1, $str.length - $index - 1);
$confirmation = Read-Host "Do you want to keep the blank on this line?"
if ($confirmation -eq 'y') {
#do action
}
Edit: Included code for multiple white spaces
Code for initial Post:
$n = #()
$f = Get-Content C:\MyPath\*.csv
foreach($item in $f) {
if($item -like "* *"){
#$res = $host.ui.PromptForChoice("Title", "want to keep the blank on this line? `n $item", [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no), 0)
$str = $item
$index = $str.IndexOf(" ")
while ($index -gt -1) {
write-host $str.Substring(0,$index) -NoNewline
write-host "_" -foreground "magenta" -NoNewline
$str = $str.Substring( $index + 1, $str.length - $index - 1);
$index = $str.IndexOf(" ")
}
write-host $str.Substring( $index + 1, $str.length - $index - 1);
$confirmation = Read-Host "Do you want to keep the blank on this line?"
if (($confirmation -eq 'y') -or ($confirmation -eq 'Y')) {
$n+=$item
}
else {
$n+=$item -replace ' '
}
} else {
$n+=$item -replace ' '
}
}
$n | Set-Content C:\MyPath\*.csv

Unable to trim/trimend "$"

I am trying to modify a PS script from online resource:
Trap {"Error: $_"; Break;}
$D = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$Domain = [ADSI]"LDAP://$D"
$Searcher = New-Object System.DirectoryServices.DirectorySearcher
$Searcher.PageSize = 200
$Searcher.SearchScope = "subtree"
$Searcher.Filter = "(objectCategory=computer)"
$Searcher.PropertiesToLoad.Add("samAccountName") > $Null
$Searcher.PropertiesToLoad.Add("lastLogon") > $Null
# Create hash table of users and their last logon dates.
$arrComp = #{}
# Enumerate all Domain Controllers.
ForEach ($DC In $D.DomainControllers)
{
$Server = $DC.Name
$Searcher.SearchRoot = "LDAP://$Server/" + $Domain.distinguishedName
$Results = $Searcher.FindAll()
ForEach ($Result In $Results)
{
$DN = $Result.Properties.Item("samAccountName")
$LL = $Result.Properties.Item("lastLogon")
If ($LL.Count -eq 0)
{
$Last = [DateTime]0
}
Else
{
$Last = [DateTime]$LL.Item(0)
}
If ($Last -eq 0)
{
$LastLogon = $Last.AddYears(1600)
}
Else
{
$LastLogon = $Last.AddYears(1600).ToLocalTime()
}
If ($arrComp.ContainsKey("$DN"))
{
If ($LastLogon -gt $arrComp["$DN"])
{
$arrComp["$DN"] = $LastLogon
}
}
Else
{
$arrComp.Add("$DN", $LastLogon)
}
}
}
Script above give me the computername & its' last logon date, however the computernames are having "$" at the end. I would like to trim the "$" in order for me to use it remove the computer from AD later. However my script is not working.
$Compdollar = $arrComp.getEnumerator() | Select-Object Key | out-string
$AllComp = #()
Foreach ($inactD in $Compdollar) {
$AllComp += $inactD.Trim("$")
}
$Allcomp
The output is still computer name with "$", can anyone tells me why it wasn't trimmed?
Don't use double quotes with a $ as it is treated like a variable. Use single quotes instead.
$AllComp += $inactD.Trim('$')
Or use the backtick to escape the dollar sign.
$AllComp += $inactD.Trim("`$")