Save matching values into a variable - arrays

I'm trying to script a "password expiry notification". I collect all users in our AD and put the date, where the password expires into an array. Now my idea is to check if todays date + 4 days is matching with one or more values from the array. It returns "Exists"
Now my question is: How can I save the matching dates/values into a variable?
$Users =#()
$Users += Get-ADUser #QueryParameters | Select-Object -Property $SelectionProperties
for($i = 0; $i -lt $Users.Count; $i ++)
{
$a = $Users[$i].PasswordExpiry
$a
}
$today = (get-date).AddDays(4).ToString('dd/MM/yyyy')
if ($Users.PasswordExpiry -contains $today)
{
write-host "Exists"
}

Use Where-Object to filter out all the users except for those who's passwords expire within 4 days, then send an email to each of those:
$now = Get-Date
$threshold = $now.AddDays(4).Date
$expiringUsers = Get-ADUser #QueryParameters | Where-Object { $_.PasswordExpiry -gt $now -and $_.PasswordExpiry -le $threshold }
foreach($user in $expiringUsers){
$expiryDate = $_.PasswordExpiry
Send-MailMessage -From noreply#domain.tld -To $user.Mail -SmtpServer smtphost.domain.tld -Subject "Password about to expire!" -Body "Your password will expire on '$expiryDate'! Change it today!"
}

Related

How do I change my powershell script to GUI

I have recently started taking online Active Directory courses and in the process, I have developed a script for moving computers between OUs. The script is fine and running, but I was wondering if someone can help me change it to GUI(exe) file.
I have looked around and it does not seem there is a good place to provide a nice explanation. Code is below:
...script searches a computer given a keyword. user selects one of the computers listed after search. script then searches an OU based on a keyword input from user. Then, then uses chooses an option of the OUs listed after search. Script then moves the computer to that OU.
Import-Module ActiveDirectory
$computerName = Read-Host -Prompt 'Enter The Name of The Computer You Want To Move'
$temp = Get-ADComputer -filter "Name -like '*$computerName*'" -Properties DistinguishedName | Select-Object -Property DistinguishedName
#$comp[0].DistinguishedName.Split("=").Split(",")[1]
Function Select-Computer
{
$counter = 0
Write-Host "Select Item"
foreach ($c in $temp)
{
write-host "$counter :" $c.DistinguishedName.Split("=").Split(",")[1]
$Counter++
}
[ValidateNotNullOrEmpty()]
$Selection = Read-Host -Prompt 'Choose the computer you want to move'
return $temp[$Selection]
}
$returned = Select-Computer
$computerLocation = $returned.DistinguishedName
$keyword = Read-Host -Prompt "Enter a keyword of the OU you want to move to"
$find = Get-ADOrganizationalUnit -Filter "Name -like '*$keyword*'" -Properties DistinguishedName | Select-Object -Property DistinguishedName
Function Select-OU
{
$counter = 0
Write-Host "Select the OU"
foreach ($ou in $find)
{
$canonicalName = Get-ADOrganizationalUnit -Identity $ou.DistinguishedName -Properties CanonicalName | Select-Object CanonicalName
write-host "$counter :" $ou.DistinguishedName.Split("=").Split(",")[1] in "$canonicalName".Split("=").Replace("#", "").Replace("{", "").Replace("}", "").Replace("CanonicalName", "")
#DistinguishedName.Split("=").Replace("OU", "").Replace(",", "/")
#
$Counter++
}
[ValidateNotNullOrEmpty()]
$Selection = Read-Host -Prompt 'Choose the target you OU'
return $find[$Selection]
}
$return = Select-OU
$targetOU = $return.DistinguishedName
Move-ADObject -Identity "$computerLocation" -TargetPath "$targetOU" -Verbose

Get values from 2 arrays

Maybe the header is wrong but i dont know how to explain.
I have 4 csv files with aprox 15000 rows in each looking like this
number,"surname","forename","emailAddress","taxIdentifier"
100238963,"Smith","John","john.smith#gmail.com","xxxxxxxxxxxx"
Im reading in 9999 of the rows and creating a json file we use on a site to check every person, we then get a respond back for most of the users, and that respons is "number"
Then i need to find all them persons in the first array.
I have done it like this today, but it take to much time to check every person like this, is there any better way of doing this?
This is the code for getting the persons from the file and create json file:
$Files = Get-ChildItem -Path "$Folders\\*" -Include *.csv -Force
foreach ($File in $Files){
$fname = $file
$fname = (Split-Path $File.name -leaf).ToString().Replace(".csv", "")
$Savefile = $fname+ "_Cleaned.csv"
$users = Import-Csv $File
$body = "{`"requestId`": `"144x25`",`"items`": ["
$batchSize = 9999
$batchNum = 0
$row = 0
while ($row -lt $users.Count) {
$test = $users[$row..($row + $batchSize - 1)]
foreach ($user in $test) {
$nr = $user.number
$tax = $user.taxIdentifier
$body += "{`"itemId`": `"$nr`",`"subjectId`": `"$tax`"},"
}
And then this is the code to deal with the respons:
$Result = #()
foreach ($1 in $response.allowedItemIds)
{
foreach ($2 in $Users){
If ($2.number -like $1)
{
$Result += [pscustomobject]#{
number = $2.number
Surname = $2.surname
Forename = $2.forename
Email = $2.emailaddress
Taxidendifier = $2.taxIdentifier
}
}
}
}
$Result | Export-Csv -path "$folders\$savefile" -NoTypeInformation -Append
$row += $batchSize
$batchNum++
Hope someone has any ideas
Cheers
I think you can just do this:
# read the original data file
$originalCsv = #"
number,"surname","forename","emailAddress","taxIdentifier"
1000,"Smith","Mel","mel.smith#example.org","xxxxxxxxxxxx"
3000,"Wilde","Kim","kim.wilde#example.org","xxxxxxxxxxxx"
2000,"Jones","Gryff Rhys","gryff.jones#example.org","xxxxxxxxxxxx"
"#
$originalData = $originalCsv | ConvertFrom-Csv
# get a response from the api
$responseJson = #"
{
"requestId": "144x25",
"responseId": "2efb8b47-d693-46ac-96b1-a31288567cf3",
"allowedItemIds": [ 1000, 2000 ]
}
"#
$responseData = $responseJson | ConvertFrom-Json
# filter original data for matches to the response
$matches = $originalData | where-object { $_.number -in $responseData.allowedItemIds }
# number surname forename emailAddress taxIdentifier
# ------ ------- -------- ------------ -------------
# 1000 Smith Mel mel.smith#example.org xxxxxxxxxxxx
# 2000 Jones Gryff Rhys gryff.jones#example.org xxxxxxxxxxxx
# write the data out
$matches | Export-Csv -Path ".\myfile.csv" -NoTypeInformation -Append
I don't know if that will perform better than your example, but it should do as it's not got a nested loop that runs original row count * response row count times.

Can I convert comma-separated string back to AD value objects?

So I'm trying to write a script that will generate an AD report based on parameters chosen by the user, and it's caused me a lot more headache than I expected. Here's an excerpt of what I'm trying to do:
#import the ActiveDirectory Module
Import-Module ActiveDirectory
#report parameter variables
$firstname = {givenname}
$lastname = {surname}
$displayname = {DisplayName}
$logonname = {sAMAccountName}
#set array initially to number of possible parameters
$inputarray = (0..3)
#display menu
cls
Write-Host "Please select the parameters you would like in your report:"
Write-Host "Enter q when finished"
Write-Host `n
Write-Host " 1) First Name"
Write-Host " 2) Last Name"
Write-Host " 3) Display Name"
Write-Host " 4) Logon Name"
Write-Host `n
#read in selections from user while input isn't Q, and not bigger than array
#bounds
for ($i=0; (($i -le 3) -and ($inputarray[$i] -ne 'q')); $i++){
$selection = ($i + 1)
$inputarray[$i] = Read-host "Enter report parameter $selection"
#exit loop for quit selection
if ($inputarray[$i] -eq 'q'){
break
}
switch ($inputarray[$i])
{
"1" { $result = $firstname }
"2" { $result = $lastname }
"3" { $result = $displayname }
"4" { $result = $logonname }
}
$inputarray[$i] = $result
}
$arraylen = $i
$test = ''
for ($x=0; $x -lt $arraylen; $x++){
if($x -lt ($arraylen -1)){
$test = ($test + $inputarray[$x] + ',')
}
else{
$test = ($test + $inputarray[$x])
}
}
Get-ADUser -searchbase "my targeted OU" -Properties * -Filter * |
Select-Object $test |
Export-Csv -Path "export path here.csv" -NoTypeInformation
I thought creating a string from the array with comma separated values would work the same as typing them in (like Select-Object givenname,surname,lastlogin) but that clearly isn't working. Any ideas how to change it back from a string value to individual objects, so maybe it will accept it?
1st problem: Statements such as $firstname = {givenname} assign a script block ({...}) to the variable, which is not your intent; instead, you're looking to assign property names as strings, e.g., $firstname = 'givenname'.
2nd problem: You're building variable $test as a string, whereas what you need to pass to Select-Object is an array of strings (property names), so you can just use $inputArray directly.

Powershell array of list objects

I am trying to write a script which takes a text file of f5 LTM results and puts this into a searchable array so i can compare results from yesterday to today.
This is an example of the file;
MemberCount : 2
Name : /Common/blah1
Availability : AVAILABILITY_STATUS_GREEN
Enabled : ENABLED_STATUS_ENABLED
Status : The pool is available
MemberCount : 2
Name : /Common/blah2
Availability : AVAILABILITY_STATUS_GREEN
Enabled : ENABLED_STATUS_ENABLED
Status : The pool is available
So ideally I would like to make Name the unique field and the sort the list so i can compare the changes in status from yesterday to today.
Here is the code I am working on to email the results but it only provides line by line difference where I would rather get the object changes in the email.
Add-PSSnapIn iControlSnapIn
$f5_hosts = '192.168.x.x', '192.168.x.x'
$uid = 'xx'
$pwd ='xx'
foreach($f5_host in $f5_hosts){
$f5_host_out = $(get-date -f yyyyMMdd)+"_"+$f5_host+".txt"
$f5_host_out_yesterday = $((get-date).AddDays(-1).ToString('yyyyMMdd'))+"_"+$f5_host+".txt"
#Check login details and generate LTM output file for $f5_host
Initialize-F5.iControl -HostName $f5_host -Username $uid -password $pwd
Get-F5.LTMPool | out-file $f5_host_out
#// Check if EMP file for yesterday exists and send results else send error
if (Test-Path $f5_host_out_yesterday){
$f5_host_Result = compare-object -ReferenceObject (Get-Content $f5_host_out) -DifferenceObject (Get-Content $f5_host_out_yesterday )
$f5_host_out_yesterday+": file is Present!"
$Text_Body = $f5_host+": difference `r`n"
$Text_Body += ($f5_host_Result | out-string)
Send-MailMessage -to simon.thomason#racq.com.au -from simon.thomason#racq.com.au -subject $f5_host+": F5 Daily LTM Check" -body $Text_Body -smtpserver mailrelay.racqgroup.local
}else{
$f5_host_out_yesterday+": is not file is Present!"
Send-MailMessage -to simon.thomason#racq.com.au -from simon.thomason#racq.com.au -subject $f5_host+": Check failed" -body "Yesterday's file is not present" -smtpserver mailrelay.racqgroup.local
}
}
#Limit File retention to 30days.
$limit = (Get-Date).AddDays(-30)
#Get script location
$path = Get-Location
# Delete files older than the $limit.
Get-ChildItem -Path $path -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit } | Remove-Item -Force
So as an output I would just want to see something like this in a email
Difference From yesterday to today
Yesterday
MemberCount : 2
Name : /Common/blah1
Availability : AVAILABILITY_STATUS_GREEN
Enabled : ENABLED_STATUS_ENABLED
Status : The pool is available
Today
MemberCount : 2
Name : /Common/blah1
Availability : AVAILABILITY_STATUS_RED
Enabled : ENABLED_STATUS_ENABLED
Status : The pool is available
Ok, on your second question, exporting and importing passwords, the encryption is done per user (and I'm pretty sure per machine), so you can't export it, and then have another account import it, but for just straight saving an encrypted password you can use these functions:
Function Out-EncryptedPasswordFile{
[cmdletbinding()]
Param(
[Parameter(Mandatory = $true)]
[string]$Password,
[Parameter(Mandatory = $true)]
[ValidateScript({If(Test-Path (Split-Path $_)){$true}else{Throw "Unable to create file, directory '$(Split-Path $_)\' does not exist."} })][String]$Path
)
ConvertTo-SecureString -AsPlainText $Password -Force | ConvertFrom-SecureString | Set-Content $Path -Encoding Unicode
}
#Usage Example
#Out-EncryptedPasswordFile TestP#ssw0rd c:\temp\password.txt
Function Import-EncryptedPasswordFile{
[cmdletbinding()]
Param(
[Parameter(Mandatory = $true)]
[ValidateScript({Test-Path $_})][string]$Path
)
$SSPassword = Get-Content $Path | ConvertTo-SecureString
$Ptr = [System.Runtime.InteropServices.Marshal]::SecureStringToCoTaskMemUnicode($SSPassword)
[System.Runtime.InteropServices.Marshal]::PtrToStringUni($Ptr)
[System.Runtime.InteropServices.Marshal]::ZeroFreeCoTaskMemUnicode($Ptr)
}
#Usage Example
#Import-EncryptedPasswordFile C:\temp\password.txt
not certain if right or wrong but this gave the result I was looking for. json was just used so I could store the object and convert it back to powershell object.
Add-PSSnapIn iControlSnapIn
$f5_hosts = 'x.x.x.x', 'x.x.x.x'
$uid = 'xx'
$pwd ='xx'
foreach($f5_host in $f5_hosts){
$f5_host_out = $(get-date -f yyyyMMdd)+"_"+$f5_host+".json"
$f5_host_out_yesterday = $((get-date).AddDays(-1).ToString('yyyyMMdd'))+"_"+$f5_host+".json"
#Check login details and generate LTM output file for $f5_host
Initialize-F5.iControl -HostName $f5_host -Username $uid -password $pwd
Get-F5.LTMPool | ConvertTo-Json | out-file $f5_host_out
#// Check if EMP file for yesterday exists and send results else send error
if (Test-Path $f5_host_out_yesterday){
$f5_host_json_today = Get-Content -Raw $f5_host_out | ConvertFrom-Json
$f5_host_json_yesterday = Get-Content -Raw $f5_host_out_yesterday | ConvertFrom-Json
$f5_host_Result = Compare-Object -ReferenceObject ($f5_host_json_today | Sort-Object ) -DifferenceObject ($f5_host_json_yesterday | Sort-Object ) -property MemberCount, Name, Status, Availability, Enabled, Status | sort-object -property Name
#$f5_host_Result
$f5_host_out_yesterday+": file is Present!"
$Text_Body = $f5_host+": difference `r`n"
$Text_Body += ($f5_host_Result | out-string)
Send-MailMessage -to y#x -from y#x -subject $f5_host+": F5 Daily LTM Check" -body $Text_Body -smtpserver blah
}else{
$f5_host_out_yesterday+": is not file is Present!"
Send-MailMessage -to y#x -from y#x -subject $f5_host+": Check failed" -body "Yesterday's file is not present" -smtpserver blah
}
}
#Limit File retention to 30days.
$limit = (Get-Date).AddDays(-30)
#Get script location
$path = Get-Location
# Delete files older than the $limit.
Get-ChildItem -Path $path -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit } | Remove-Item -Force

Powershell - combining arrays

I am new to powershell and in need of help. My first script for work is automate the new and termed users in AD environment.
A CSV dump will be done once daily from our Peoplesoft system. I use Import-CSV and create 3 arrays (new, term and processed).
The trouble I'm having is with combining the 3 arrays once i loop through all the users and try putting it back into the file. The code breaks at the $New += $Term lines. I believe this is due to the fact that there is only 1 record of each user type (new, term and processed) in my test file (I know, add more users…can't. This may be a real world outcome for any particular day). Below is my sample code:
#Get Credentials from user
$c = Get-Credential
#Get Date for $Term array population
$e = Get-Date -format M/d/yyyy
#Set file location and variable for said file
$File = "c:\users\nmaddux\desktop\adduserstuff\test.csv"
#Import record sets for New and Term users
$New = #()
$Term = #()
$Procd = #()
$New = Import-Csv $File | Where-Object {
$_.TermDate -eq "" -and $_.LastName -ne "" -and $_.Processdate -eq ""
}
$Term = Import-Csv $File | Where-Object {
$_.TermDate -ne "" -and $_.Processdate -eq "" -and $_.TermDate -le $e
}
$Procd = Import-Csv $File | Where-Object { $_.Processdate -ne "" }
#Process both new and term users provided there are records to process for each
If ($New -ne $NULL -and $Term -ne $NULL) {
# Some code to process users
}
$new += $term
$new += $Procd
$new | Export-Csv $file -NoTypeInformation -ErrorAction SilentlyContinue
So it will export but only partial results.
error - Method invocation failed because [System.Management.Automation.PSObject] doesn't contain a method named 'op_Addition'.
If Import-Csv only returns 1 result, then you are correct that your variable is assumed NOT to be an array, then concatenation will fail. This is not change by the fact that you have pre-initialized your variables with #(). In fact, that step isn't necessary.
To force the result to be treated as an array, you can either wrap your whole Import-Csv line in #(), or do something similar afterward.
$new = #( Import-Csv $File | Where-Object {...} )
# or
$new = Import-Csv $File | Where-Object {...}
$new = #($new)
So you are importing the same CSV file 3 times? isn't it better to import it once and then set the arrays to be filtered "views" of it?
Sort of like this. You should also be able to use the "Count" value from each array as well to say whether 1 or more results were returned.
#Get Credentials from user
$c = Get-Credential
#Get Date for $Term array population
$e = Get-Date -format M/d/yyyy
#Set file location and variable for said file
$File = "c:\users\nmaddux\desktop\adduserstuff\test.csv"
#Import record sets for New and Term users
[array]$New
[array]$Term
[array]$Procd
[array]$Import = Import-Csv $File
[array]$New = $Import | ? {$_.TermDate -eq "" -and $_.LastName -ne "" -and $_.Processdate -eq ""}
[array]$Term = $Import | ? {$_.TermDate -ne "" -and $_.Processdate -eq "" -and $_.TermDate -le $e}
[array]$Procd = $Import | ? {$_.Processdate -ne ""}
#Process both new and term users provided there are records to process for each
if (($New.Count -gt 0) -and ($Term.Count -gt 0))
{
# Some code to process users
}
$new += $term
$new += $Procd
$new | Export-Csv $file -NoTypeInformation -ErrorAction SilentlyContinue
You can also enforce the type by typecasting the variable:
$array = #()
$array = gci test.txt
$array.GetType()
[array]$array = #()
$array = gci test.txt
$array.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True FileInfo System.IO.FileSystemInfo
True True Object[] System.Array
I know I'm coming into this discussion late, but for someone else that comes along...
Since you already defined $new as an empty array, when you import from the csv you want to ADD the output to your pre-defined array, not set it equal to the output of import-csv.
$new = #()
$new += Import-Csv $File | Where-Object {
$_.TermDate -eq "" -and $_.LastName -ne "" -and $_.Processdate -eq ""
}

Resources