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
Related
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!"
}
Is there a faster way to get a specific registry value from a list of servers? I'm selecting a text file of computers with different flavors of windows and getting the OS product name. I'm finding that it's taking a couple seconds per computer to retrieve.
Current script:
Clear-Host
# Prompt for file containing list of target
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$myDialog = New-Object System.Windows.Forms.OpenFileDialog
$myDialog.Title = "Select File of Target Systems"
$myDialog.InitialDirectory = $PSScriptRoot
$myDialog.Filter = "TXT (*.txt) | *.txt"
$result = $myDialog.ShowDialog()
If ($result -eq "OK") {
$Computers = Get-Content $myDialog.FileName
}
Else {
Write-Host "`nCancelled by User`n"
}
$Array = #()
# Loop Through Computers
ForEach ($Computer in $Computers) {
Write-Warning "Processing $Computer"
# Get Registry Values
Try {
$OSVersion = Invoke-Command -ComputerName $Computer -ScriptBlock { (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name ProductName).ProductName }
# Create a custom object
$ComplexObject = New-Object PSCustomObject
$ComplexObject | Add-Member -MemberType NoteProperty -Name "Server name" -Value $Computer
$ComplexObject | Add-Member -MemberType NoteProperty -Name "OS Version" -Value $OSVersion
# Add custom object to our array
$Array += $ComplexObject
}
Catch {
$_.Exception.Message
Break
}
}
# Results
If ($Array) {
# Display results in new window
$Array | Out-GridView -Title "OS Version Results"
# Display results in PS console
$Array
}
My end goal later on in the script is to do different things based on the OS version so I want to separate them into independent lists:
If (We have Win2008 servers) {
"Do This"
}
If (We have Win2012R2 servers) {
"Do This"
}
Clear-Host
# Prompt for file containing list of target
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$myDialog = [System.Windows.Forms.OpenFileDialog]::new()
$myDialog.Title = "Select File of Target Systems"
$myDialog.InitialDirectory = $PSScriptRoot
$myDialog.Filter = "TXT (*.txt) | *.txt"
$result = $myDialog.ShowDialog()
If ($result -eq "OK") {
$Computers = Get-Content $myDialog.FileName
}
Else {
Write-Host "`nCancelled by User`n"
}
# Get Registry Values
$Array = Try {
Invoke-Command -ComputerName $Computers -ScriptBlock {
(Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name ProductName).ProductName
} -ErrorAction stop | Select-Object #{n="Server Name";e={$_.pscomputername}},
#{n="OS Version";e={$_}}
}
Catch {
write-warning $_.Exception.Message
break
}
# Results
If ($Array) {
# Display results in new window
$Array | Out-GridView -Title "OS Version Results"
# Display results in PS console
$Array
}
You can use Get-AdComputer like:
Get-ADComputer -Filter {(OperatingSystem -like "*windows*server*") -and (Enabled -eq "True")} -Properties OperatingSystem | Select -ExpandProperty OperatingSystem | ForEach {
If($_ -match "Windows Server 2008.*"){
# Server 2008
}
If($_ -match "Windows Server 2012.*"){
# Server 2012
}
# Add more like 2016,2019
}
This question already has an answer here:
Pass object[] into a function in PowerShell
(1 answer)
Closed 4 years ago.
I'm building a tool that will scan my files and a friend's files. We will use this to make sure we have the same files in our databases. The script I have so far has a variable input issue. For some reason, the PowerShell script fails on my drive letter input. Anyone have any ideas?
Here is my script:
{
function Show-Menu {
param (
[string] $Title = "Andy's Manual Database Tool"
)
Clear-Host
Write-Host ""
Write-Host "================ $Title ================"
Write-Host ""
Write-Host -f Green "1. Andys Files listing"
Write-Host -f green "2. Reids files listing"
Write-Host -f Red "3. Dark Matter Testing"
Write-Host "4. Convert .txt to .csv"
Write-Host "5. Convert Blank File to .csv"
Write-Host "6. Convert .csv to .txt"
}
Function Body {
Show-Menu
Write-Host ""
$Input = Read-Host "Please make a selection"
if ($Input -eq "1") {
Clear-Host
$root = Read-Host -Prompt 'Specify the location of Database? Example format: C:\*'
$y = read-host -Prompt 'Input file types. Format Example: " .jpg,.mp4,.mp3,.pdf .... " Do * for all'
$z = Read-Host -Prompt 'Your Save file name will be? Examples: Movies database , Music database , audiobooks.'
Get-ChildItem -Path $root -File -Recurse *.$y | Select-Object -Property Name | Export-Csv -NoTypeInformation $z Andy.csv
}
if ($Input -eq "2") {
Clear-Host
$root = Read-Host -Prompt 'Specify the location of Database? Example format: C:\*'
$y = read-host -Prompt 'Input file types. File Format Examples: " .jpg,.mp4,.mp3,.pdf .... " Do * for all'
$z = Read-Host -Prompt 'Your Save file name will be? Examples: Movies database , Music database , audiobooks.'
Get-ChildItem -Path $root -File -Recurse *.$y | Select-Object -Property Name | Export-Csv -NoTypeInformation $z' From Reid.csv'
}
if ($Input -eq "3") { Get-Process | Stop-Process }
if ($Input -eq "4") {
Clear-Host
Get-ChildItem *.txt | rename-item -newname { $_.name -replace ".txt",".csv" }
}
if ($Input -eq "5") {
Clear-Host
Get-ChildItem * -Exclude *.ps1,*.CSV,*.TXT | rename-item -newname { "$($_.name).CSV" }
}
if ($Input -eq "6") {
Clear-Host
Get-ChildItem *.csv | rename-item -newname { $_.name -replace ".csv",".txt" }
}
Write-Host 'Complete! ^_^'
Start-Sleep -seconds 5
Body
}
Body
}
This issue is here, from the script above:
$root = Read-Host -Prompt 'Specify the location of Database? Example format: C:\*'
$y = read-host -Prompt 'Input file types. Format Example: " .jpg,.mp4,.mp3,.pdf .... " Do * for all'
$z = Read-Host -Prompt 'Your Save file name will be? Examples: Movies database , Music database , audiobooks.'
Get-ChildItem -Path $root -File -Recurse *.$y |
Select-Object -Property Name |
Export-Csv -NoTypeInformation $z Andy.csv
I'm using $root as an input for my drive letter or location path which is the problem.
There a few minor issues.
$z Andy.csv Needs to be changed to "$z Andy.csv". You will notice when you ran this before you would of received the following message:
Export-Csv : Cannot bind parameter 'Delimiter'. Cannot convert value
"Andy.csv" to type "System.Char". Error: "String must be exactly one
character long."
Get-ChildItem -Path $root -File -Recurse *.$y Needs to be changed to Get-ChildItem -Path "$root" -Recurse -Include "*$y" - You are prompting the user to put in file extension with .<extension> (assigned to $y) then you are trying to filter with $y
Ex:$y = read-host -Prompt 'Input file types. Format Example: " .jpg,.mp4,.mp3,.pdf .... " Do * for all'
Issue: "*.$y" this would equal "..extension". Your results would then be 0 (unless you had a file with ..jpg or something like that)
Corrected:
if ($Input -eq "1") {
Clear-Host
$root = Read-Host -Prompt 'Specify the location of Database? Example format: C:\*'
$y = read-host -Prompt 'Input file types. Format Example: " .jpg,.mp4,.mp3,.pdf .... " Do * for all'
$z = Read-Host -Prompt 'Your Save file name will be? Examples: Movies database , Music database , audiobooks.'
Get-ChildItem -Path "$root" -Recurse -Include "*$y" | Select Name | Export-Csv -NoTypeInformation "$z Andy.csv"
}
You should be able to correct the rest of your script following the "Corrected" example.
EDIT:
Credit to TheIncredible1
There are automatic variables that should never be used other than for their intended purpose. In your case $Input:
Contains an enumerator that enumerates all input that is passed to a
function. The $input variable is available only to functions and
script blocks (which are unnamed functions). In the Process block of a
function, the $input variable enumerates the object that is currently
in the pipeline. When the Process block completes, there are no
objects left in the pipeline, so the $input variable enumerates an
empty collection. If the function does not have a Process block, then
in the End block, the $input variable enumerates the collection of all
input to the function.
However, in this case it should not effect your outcome. But, it is a very bad habit to get into and is best to be avoided.
This is driving me crazy. I'm a beginner at Powershell, but I can't see what's wrong here.
I'm basically running a script which goes off and checks the description field in AD, looks for a match for the user input and pulls back the PC name and the associated description. As below:
import-module activedirectory
$User = Read-Host -Prompt "Enter User's First OR Surname OR partial String"
$User = '*' + $User + '*'
Get-ADComputer -Filter 'Description -like $User' -Properties Description | Select Name, Description
write-Host "================================================================="
}
This on it's own works fine. However I want to loop this until it is closed, so the user can do another search straight away. When I put this into a loops, the first time the search is ran, no results are returned. The 2nd time the first lot of results are returned with the 2nd lot. From then on it works as normal.
So the output looks something like:
Enter User's First OR Surname OR partial String: Test
=================================================================
Enter User's First OR Surname OR partial String: Test
Name Description
---- -----------
COMPXXX01 Test, PS
COMPXXX01 Test, PS
=================================================================
So essentially the first lot of results come through on the second run.
I've tried a few ways of looping for example:
import-module activedirectory
While($true)
{
$User = Read-Host -Prompt "Enter User's First OR Surname OR partial String"
$User = '*' + $User + '*'
Get-ADComputer -Filter 'Description -like $User' -Properties Description | Select Name, Description
write-Host "================================================================="
}
Any advice would be greatly received.
Thanks
Interesting find. As far as I can tell there is some sort of bug with the console & Get-ADComputer. The Get-Adcomputer does "work" in getting the info (put in a variable and the contents show there) but will not output. Funny if you put the output twice it will work the 2nd time:
While($true)
{
$User = Read-Host -Prompt "Enter User's First OR Surname OR partial String"
$User = '*' + $User + '*'
Get-ADComputer -Filter 'Description -like $User' -Properties Description | Select Name, Description
Get-ADComputer -Filter 'Description -like $User' -Properties Description | Select Name, Description
write-Host "================================================================="
}
A work around would be to have a flag for the 1st time:
$firstTime = $true
While($true)
{
$User = Read-Host -Prompt "Enter User's First OR Surname OR partial String"
$User = '*' + $User + '*'
$computerTable = Get-ADComputer -Filter 'Description -like $User' -Properties Description | Select Name, Description
if($firstTime){
$firstTime = $false
$computerTable
}
$computerTable
write-Host "================================================================="
}
I want to specify a source user and capture the list of the groups that user belongs to. I want to then specify a second user and have the original captured groups added to the new users account.
If user1 is a member of GroupA, GroupB, GroupC I want the script to capture that and add it to user3 when I type his name.
Import-Module ActiveDirectory
$SourceUser = Read-Host -Prompt 'Enter the username of the user whos groups you would like to copy'
$DestUser = Read-Host -Prompt "Enter the username of the user who will get: $SourceUser groups"
$SourceGroups = Get-ADUser $SourceUser -Property MemberOf | % {
$_.MemberOf | Get-ADGroup | select Name | sort name
}
"List of groups for user $DestUser BEFORE script: "
Get-ADUser $DestUser -Property MemberOf | % {
$_.MemberOf | Get-ADGroup | select Name | sort name
}
# ***Where my problem is***
#Add-ADGroupMember $SourceGroups –Member $DestUser
"List of groups for user $DestUser AFTER script: "
Get-ADUser -Identity $DestUser -Property MemberOf | % {
$_.MemberOf | Get-ADGroup | select Name | sort name
}
When in doubt, read the documentation.
Syntax
Add-ADGroupMember [-Identity] <ADGroup> [-Members] <ADPrincipal[]> ...
The -Identity parameter (the first argument) takes a single group identity, not a list of identities. To add a user to a list of groups you need a loop:
$SourceGroups | ForEach-Object { Add-ADGroupMember $_ –Member $DestUser }
Here is the corrected code that now works.
$T2FolderExists = Test-Path "C:\T2\"
$LogsFolderExists = Test-Path "C:\T2\Logs\"
IF ($T2FolderExists -eq $False)
{
New-Item C:\T2\ -type directory
IF ($LogsFolderExists -eq $False)
{
New-Item C:\T2\Logs\ -type directory
}
ELSE
{
}
"`n"
Start-Transcript -Path C:\T2\Logs\CopyMembershipFromADAccount.txt -Append
"`n"
}
ELSE
{
"`n"
Start-Transcript -Path C:\T2\Logs\CopyMembershipFromADAccount.txt -Append
"`n"
}
<#*****Session Paramaters*****>
Set-ExecutionPolicy Unrestricted -force
Import-Module ActiveDirectory
Clear-Host
Write-Host "Enter the username of the user whos groups you would like to copy" -foregroundcolor Green -backgroundcolor Black
$SourceUser = Read-Host
"`n"
Write-Host "Enter the username of the user who will get: $SourceUser groups" -foregroundcolor Green -backgroundcolor Black
$DestUser = Read-Host
"`n"
$SourceGroups = Get-ADUser $SourceUser -Property MemberOf | ForEach-Object {
$_.MemberOf | Get-ADGroup | select Name -ExpandProperty Name | sort name
}
"`n`n"
Write-Host "List of groups for user $SourceUser BEFORE script: "
Get-ADUser $SourceUser -Property MemberOf | ForEach-Object {
$_.MemberOf | Get-ADGroup | select Name -ExpandProperty Name | sort name
}
"`n`n"
Write-Host "List of groups for user $DestUser BEFORE script: "
Get-ADUser $DestUser -Property MemberOf | ForEach-Object {
$_.MemberOf | Get-ADGroup | select Name -ExpandProperty Name | sort name
}
"`n`n"
ForEach ($Group in $SourceGroups)
{
Add-ADGroupMember $Group –Member $DestUser
Write-Host "Adding $Group to $DestUser 's Account" -foregroundcolor Cyan -backgroundcolor Black
}
"`n`n"
Write-Host "List of groups for user $SourceUser BEFORE script: "
Get-ADUser $SourceUser -Property MemberOf | ForEach-Object {
$_.MemberOf | Get-ADGroup | select Name -ExpandProperty Name | sort name
}
"`n`n"
Write-Host "List of groups for user $DestUser AFTER script: "
Get-ADUser $DestUser -Property MemberOf | ForEach-Object {
$_.MemberOf | Get-ADGroup | select Name -ExpandProperty Name | sort name
}
"`n"
Stop-Transcript
Exit
This is what I did:
Import-Module ActiveDirectory
Import-CSV "data.csv" -Delimiter ";" |
ForEach {`
$_.Group1,$_.Group2,$_.Group3,$_.Group4,$_.Group5,$_.Group6 |
Add-ADGroupMember `
-Members $_.Alias`
}
You can have more groups in the script without using them.
And in the "data.csv", you put this (for example):
Alias;Group1;Group2;Group3;Group4
l.salvador;Spanish;Republic;Central;WWW
Although I had an error with "Identity" could not be checked, it worked.