Server Reboot Script

Running a little low on content this last few months, plus I’ve been busy with other work stuff.

I had the requirement to create a PowerShell script that would get the uptime of a server and then decide whether or not the server needed rebooting.

I also wanted the script to randomize the reboot of the servers, that way if there are multiple servers that need rebooting at once, they don’t cause a power spike or resource issues on the hosts. I did this by creating a random number between 1 and 5 and then if the number equals 5, the server is rebooted. If not then the server isn’t rebooted.

This is the script that I ended up with and what is currently being tested:

$loglocation = "C:\scripts\reboot\log"
$dateforfile = Get-Date

#GETS UPTIME IN DAYS
$lastbootuptime = Get-WmiObject win32_operatingsystem
$uptime = (Get-Date) - ($lastbootuptime.converttodatetime($lastbootuptime.lastbootuptime))
$uptimeindays = $uptime.days

#GETS RANDOM NUMBER
$randomnumber = Get-Random -Minimum 1 -Maximum 6

if ($uptimeindays -ge "14"){

 Add-Content -Path "$loglocation\$env:COMPUTERNAME.txt" -Value @"
=====================================================================================
Server restarted at:
$dateforfile
This was an immediate shutdown as the server had been up for $uptimeindays days
"@

 Restart-Computer -Force

}elseif ($uptimeindays -lt "14" -and $uptimeindays -ge "7"){

    if ($randomnumber -eq "5"){

        Add-Content -Path "$loglocation\$env:COMPUTERNAME.txt" -Value @"
=====================================================================================
Server restarted at :
$dateforfile
This was a random restart as uptime was only $uptimeindays days
"@
        Restart-Computer -Force
    }else{

        Add-Content -Path "$loglocation\$env:COMPUTERNAME.txt" -Value @"
=====================================================================================
Server NOT restarted
$dateforfile
This was not randomly restarted. Uptime is currently $uptimeindays days. Random number was $randomnumber
"@
    }
}else{

Add-Content -Path "loglocation\$env:COMPUTERNAME.txt" -Value @"
=====================================================================================
No restart required
$dateforfile
No restart required since uptime is only $uptimeindays days
"@
}

The first time I created this script and set it up as a scheduled task, nothing happened. Turns out that I needed the -Force parameter in order for the server to be rebooted.

This will later be used in a group policy without the log creating as that is only necessary in the testing stage.

Enjoy!

 

Games In PowerShell – Version 4

Quite a large update to my Games in PowerShell project, adding a new game, changing some of the styling of the game and changing the loading screen of the game.

You’ll be pleased and proud to know that the games code is now over a thousand lines long…At least fake being proud then.

As mentioned earlier, I have added a new game. This one is different from my other games because it is a text based adventure game about a dino that needs capturing. The player only successfully completes the game once they have captured the dino and also not been killed by it. Tall order, I know.

I also changed how the Quick Fire Character Counting game works. It now starts by giving the player a full second to see the word and guess how many characters it contains, then, each time the user gets a correct answer reduces the viewing time of the word by 30 miliseconds. That way the game gradually gets more difficult instead of being stuck at the same length, which is boring.

Here is the link to my code for the game

I will be updating my project page with this update as well. Enjoy!

More Auto-Loading, Custom PowerShell Scripts

In this update, you’ll see how I added two or three new scripts to the environment and also changed how most of the scripts work.

One new script that I add was a script that would take CSV data and add the users to a group in Active Directory. You can see this below:

function Get-CSVAddToGroup{
 <#
 .SYNOPSIS
 This function allows you to add usernames from a CSV to a specified Active Directory group.
 It needs confirmation for each addition to a group.

 .EXAMPLE
 Get-CheckCSVAddToGroup -Groupname "all rossendale staff" -path c:\testing\test.csv

 #>
 [cmdletbinding()]
 param(
 [parameter(Mandatory=$true)]
 [string]$groupname,

 [parameter(mandatory=$true)]
 [string]$path
 )

 $csvformembership = Import-Csv -Path $path

 foreach ($b in $csvformembership){
  $name = $b.user
  Add-ADGroupMember -Identity "$groupname" -Members $name -Confirm
 }
}

Another script I added was my script for sending messages to remote computers on the network. You can see this below:

function Send-Message{

 function send-messagewithname{
  $name = Read-Host "Enter a name"
  $msg = Read-Host "enter a message"
  $adcompstocheck = Get-ADComputer -LDAPFilter "(name=$name)" -Properties name

  if ($adcompstocheck -eq $null){
   Write-Host "$name doesn't exist!"
   pause
  }else{
   Invoke-WmiMethod -Path win32_process -Name create -ArgumentList "msg * $msg" -ComputerName "$name"
  }
 }

 function send-messagewithip{
  $ip = Read-Host "enter an IP"
  $msg = Read-Host "enter a message"
  $adcompstocheckip = Get-ADComputer -Filter * -Properties ipv4address, name | select ipv4address
  if ($adcompstocheckip -eq $null){
   write-host "$ip doesn't exist"
   pause
  }else{
   Invoke-WmiMethod -Path win32_process -Name create -ArgumentList "msg * $msg" -ComputerName "$ip"
  }
 }

 do {$nameorip = Read-Host "Do you want to use IP or Name (I or N)?"} while (("i","n") -notcontains $nameorip)
 if ($nameorip -eq "n"){
  send-messagewithname
 }else {
  send-messagewithip
 }
}

Finally, my last script that was added to my custom PowerShell environment allowed me to list all the computers on the network with a specific operating system. For example “2012” or “Professional”. The code for this is below:

function list-OSVersonComputers{
 <#
 .SYNOPSIS
 This function allows you to list all of the computers with the requested OS version.

 .EXAMPLE
 List-OSVersionComputers -OSVersion 2012

 .EXAMPLE
 List-OSVersionComputers -OSVersion standard
 #>
 [cmdletbinding()]
  param(
  [parameter(mandatory=$true)]
  [string]$OSVersion
 )

 $OSVersion2 = '*' + ($OSVersion) + '*'

 Get-ADComputer -Filter {OperatingSystem -like $OSVersion2} -Property * | Format-Table name,operatingsystem,lastlogondate -Wrap -AutoSize
}

I also added help menus to most of my scripts to make them more professional.

My Custom, Auto-Loading PowerShell Scripts

Wanted to add onto a post I made about 10 days ago about creating a custom PowerShell environment. I currently have 3 custom commands for user management, 2 custom commands for group management and 1 command to list these (and the “homepage”) for my PowerShell prompt. So for my first example below, in the custom PowerShell prompt I would use “list-users”.

Users

I suppose I will kick off an just start with my custom commands for user management. By the way, if you didn’t know the name of the function in the PowerShell custom command is the actual commandlet you use in the custom PowerShell prompt.

First of all, I wanted a simple and quick way of getting the all users that are enabled and sorting them, then outputting them to the OGV. The following is what I used:

function list-users{
 Get-ADUser -Filter {enabled -eq $true} | sort | select name, samaccountname | ogv
}

Nice and simple, right…?

Next, I wanted to get the actual location within Active Directory as to where the user account is stored. I also wanted to be able to use a username or a full name in order to search for this information. You can see my code below:

function list-userlocation{

 function list-useragainstusername{
  $username = Read-Host "Input a username"

  $checkingAD = Get-ADUser -LDAPFilter "(samaccountname=$username)"
  if ($checkingAD -eq $null){
   Write-Host "$username does not exist!"
   pause
  }else{Get-ADUser -Identity $username | select distinguishedname }
 }

 function list-useragainstfullname{
  $fullname = read-host "input a full name"
  $checkingAD = get-aduser -ldapfilter  "(name=$fullname)"
  if ($checkingad -eq $null){
   write-host "$fullname does not exist!"
   pause
  }else{get-aduser -ldapfilter "(name=$fullname)" | select Distinguishedname}
 }

 do {$selection = read-host "check against full name or username (F or U)?"} while (("f","u") -notcontains $selection)
 if ($selection -eq "f"){
  list-useragainstfullname
 }elseif ($selection -eq "u"){
  list-useragainstusername
 }
}

Just in case you want to know, the output for the above command will look something like this “CN=NAME,OU=First OU,OU=Second OU,DC=sanderson,DC=lan”.

Finally, I have a command which allows me to get the the group membership of a user and then output that to a file. I have also made this one so that I can use the full name of a user as well as their username.

function list-usermembership{

 function list-usermembershipfromusername{
  $username = read-host "Input a username"

  $checkingAD = Get-ADUser -LDAPFilter "(samaccountname=$username)"
  if ($checkingAD -eq $null){
   Write-Host "$username does not exist!"
   pause
  }else{
   Write-Host "File with membership has been output to the desktop"
   Get-ADPrincipalGroupMembership $username | sort | select name | Out-File -FilePath  "c:\users\YOU!\desktop\$username Group Membership List.txt" -Append
  }
 }

 function list-usermembershipfromfullname{
  $fullname = Read-Host "Input a fullname"

  $checkingAD = Get-ADUser -LDAPFilter "(name=$fullname)"
  if ($checkingAD -eq $null){
   write-host "$fullname does not exist!"
   pause
  }else{
   $fullnameresolved = Get-ADUser -LDAPFilter "(name=$fullname)";
   $filename = $fullnameresolved.SamAccountName;
   Get-ADPrincipalGroupMembership -Identity $fullnameresolved | sort | select name | Out-File "c:\users\YOU!\desktop\$filename Group Membership List.txt" -Append;
   Write-Host "file with membership has been output to the desktop"
  }
 }

 do {$selection = Read-Host "Do you want to use fullnames or usernames? (F or U)"} while (("F","u") -notcontains $selection)
 if($selection -eq "f"){
  list-usermembershipfromfullname
 }elseif ($selection -eq "u"){
  list-usermembershipfromusername
 }else {}

}

Again, just in case you wanted to know, this outputs to a text document that will roughly ressemble the following:

name

—–

Group #1

Group #2

This allows me to quickly see if multiple users are part of a group and also to get a reference of group membership before disabling a user and removing them from all of their groups.

Groups

Lets move onto group management automation. This area is a little less sparse because, well… in my experience atleast, users are dumber than groups.

Again, a very simple one to start off with. This one simply lists all of the groups on the domain, selects certain attributes of the groups, sorts them and then ouputs them to OGV.

Below is the code for that:

function list-groups{

 Get-ADGroup -Filter * | select distinguishedname, name | sort | ogv

}

As I said, the group side of things is a little sparse. As in I only have two custom commands for them.

My other custom command for Active Directory groups collects me the membership information for that group. Like the users, I also make sure that my input matches a group within AD. Below is my code:

function list-groupmembership{
 $groupname = Read-Host "What group do you want to check?"

 $adlistforgroupcheck = Get-ADGroup -LDAPFilter "(name=$groupname)"

 if ($adlistforgroupcheck -eq $null){
  Write-Host "$groupname does not exist"
  pause
 }else {
  Get-ADGroupMember -Identity $groupname | select name, samaccountname | sort name | ogv
 }
}

Re-Displaying the Custom Commands

Now, an issue I ran into when I added these to my PowerShell environment was that when I had ran a command, or used clear-host or something along those lines. It meant that I could no longer see my custom commands, making them useless since I couldn’t remember what I had called the bloody things 🙂

That’s why I create a new command that would imitate what the prompt looks like when I first load it up. You can see my code below:

function list-customcommands{

 write-host @"
Custom PowerShell Environment Loaded
Go to '$profile' for config changes
go to 'documents\windowspowershell\scripts\autoload to add new scripts'

List of commands:             |
USERS                    |   GROUPS
List-users | ogv            |   list-groups | ogv
list-usermembership | file to desktop     |   list-groupmembership | ogv
list-userlocation            |
|
"@

}

Bit of a long post I know, but necessary background on some examples for custom PowerShell environments and Active Directory automation.

Enjoy!

Custom PowerShell Environment and Modules

To add custom PowerShell modules to your PowerShell environment, you first need to find out where you PowerShell profile is. You can do this by typing in:

$profile

into a PowerShell prompt. It should look like the following:

$profile

Now we need to test if the path actually exists. To do this type:

Test-Path $profile

If the prompt returns $true, your good. If it returns $false then you will need to run the following command:

New-Item -Path $profile -Itemtype file -Force

Now that is done we should be able to open the file in notepad. You can either browse to it following the path in $profile or type in:

notepad $profile

This should open a blank text file:

$profile empty

In here is where you specify PowerShell to look for custom modules and can even add text to the PowerShell prompt. For example, if I had the following to the notepad, it will also be displayed when I open a new PowerShell window:

custom $profile (write-host)

But we can also use this to load custom functions into our PowerShell environment. To do this, got to the $profile location, here you should find a folder called “Scripts”

Scripts folder

Go into the folder and create a new folder, mine is called “autoload”

autoload

Here is where you create your custom functions/ scripts. For example, here is what I have:

list-example

where each file contains a single function.

Now that were done with the easy part. You want to go back into your $profile notepad and add the following in order for PowerShell to load your customer functions:

$psdir="C:\users\YOU!\documents\windowspowershell\scripts\YOURSCRIPTFILENAME"

get-childitem "${psdir}\*.ps1" | %{.$_}

You can see from my file. I have multiple files to make for easier sorting of my functions. I also have a custom start screen to list all of my current commands so that I don’t forget them. You can see that below:

$profile example

Just so that you can see, this is what my PowerShell prompts look like:

My prompt

For a list on verbs and commands that you can use, visit the Microsoft website and forums. You DON’T want to use verbs or commands that are used else where or that have a unique purpose. Choose verbs that are different. Good luck. Enjoy!

Secure Password Generation In PowerShell

Nice simple one today. Very short and easy. Just made a big post to my job automation project which is why I made the following script in PowerShell. Basically what I needed was to be able to reset multiple user account passwords in Active Directory. This meant that I needed to generate secure passwords, this was to ensure that they would meet the minimum requirements, and also convert them to something that PowerShell and Active Directory “liked”.

The below 3 lines of code should do the trick:

[string]$initialpassword = ([char[]](Get-Random -input $(47..57 + 65..90 +97..122) -count 8)) + (Get-Random -minimum 0 -maximum 10)

$passwordwithspacesremoved = $initialpassword.Replace(' ','')

$convertedpassword = ConvertTo-SecureString -AsPlainText $passwordwithspacesremoved -Force

I had the add the extra “Get-Random -Minimum 0 -Maximum 10” because, since its randomly generated, sometimes it didn’t include a single number. This obviously would make the password not secure enough to be used within Active Directory. So rather than waste time trying to define the randomness to include some sort of number, I simply made damn sure that there would always be a random digit at the end. Both ensuring sufficient security to be used in Active Directory and also still being random. (Wouldn’t be good if all the passwords ended in 3 :p )

How I used this code, only if you’re interested though is like this:

Set-ADAccountPassword -Identity $USERNAME$ -Reset - NewPassword $convertedpassword -PassThru | Enables-ADAccount | Unlock-ADAccount

As you can see, I have passed the secure string password into the account reset command. Works like a charm. Some of the other parameters (such as -PassThru) stops the process being weird /breaking.

Enjoy!

Changing Script Execution Policies with Powershell

First, load up a Powershell prompt in Administrator mode.

Using the command:

Get-ExecutionPolicy -List

will bring up all of the execution policies for the different scopes. This can be seen in the screenshot below.

1

Now that we have our existing execution policies, we want to change them so that scripts can be run on your machine. The two we’re interested in is the “CurrentUser” and the “LocalMachine” scopes.

Given this, we want to change the execution policy for these two to be “RemoteSigned”. This way only the scripts that you authorize will be run, instead of the “Unrestricted” option with would allow Powershell to run ANY script. Obviously this could be a large security risk. With RemoteSigned, it also means that any downloaded scripts will need to be trusted in order to work.

Using the command:

Set-ExecutionPolicy RemoteSigned

When you are presented with the following screen, you want to select the “Yes to All” option or type “A”

2

Finally, we can run the command:

Get-ExecutionPolicy -List

Again which will show that the execution policy has now been changed.

3

You should now be able to run Powershell scripts. Please note that you may need to load then in Powershell ISE and run them which should ask you to make the script trusted or not.