Tag Archives: Powershell

Powershell – How to delete files and folders older than a date

Ok. so this seems pretty simple but sometimes can be daunting figuring out how to actually delete files and folders (recursively). In the script below, all you need to do is define the variables for the directory and how old do you want to go back. Simply change the $directory and $OlderThan variables and kick the script off. Actually, I’ve got a line commented out for you to run this script in “WhatIf” mode. In WhatIf mode, the script simply tells you what it would do if you ran it. Pretty cool!

# Delete items older than a date including subfolders
$directory = 'e:\path\to\what\you\want\to\delete' 
$OlderThan = 365
# To do a Whatif: Get-ChildItem $directory | Where-Object {$_.CreationTime -le (Get-Date).AddDays(-$OlderThan)} | Foreach-Object { Remove-Item $_.FullName -Recurse -Verbose -Force -whatif}
Get-ChildItem $directory | Where-Object {$_.CreationTime -le (Get-Date).AddDays(-$OlderThan)} | Foreach-Object { Remove-Item $_.FullName -Recurse -Verbose -Force}

How To: Powershell Get Local Administrators and Active Directory Nested Groups – SOX

For the past few years, I’ve been tasked with ensuring that our in-scope servers for Sarbanes-Oxley (SOX) have the correct users and groups in them. Before we would have to screen shot the members of the local admins and all the groups that are in the local admins. I decided to take the time to create a script that gets the local admins of the server and write the output to a transcript file.

Script to get Server Local Administrators:

I named the script Get-ServerLocalAdministrators:

Get-ServerLocalAdministrators.ps1 -domain domain -computername computername

The steps the PowerShell script goes through are:

  • Get domain and server
  • Import PowerShell Modules
  • Get Date and Time
  • Get the working directory
  • Convert the domain and computername to upper and lower for pattern matches
  • Start the transcript in the current directory and use some variables for naming the transcript
  • Get the server’s local administrators object using Get-WmiObject and a where cause for the group component
  • Get the members of the local admins and determine if it’s a user or group.
  • If user, put it in the transcript and tag the user as uer
  • If Group, put the group in a variable to loop through later
  • In the transcript, I’m doing a Write-Host to tag each group. If the group has a sub group, (nested group), then put that in another variable to loop through that as well

Since we have Group Memebers that are cross domain, I had to include the -Server paramater get the object’s distinguished Name and use that for -Server.
The only line you have to change in this script (because I was lazy) is to change line 32 to your domain. So, if your server FQDN is server.subdomain.domain.com you would call this “Domain.com” if it was simply “server.domain.com” then you would call this “com”.

[CmdletBinding()]
Param(
    [Parameter(Mandatory=$True,Position=1)]
        [string]$domain,
    [Parameter(Mandatory=$True,Position=2)]
        [string]$computername
    )

# Importing Modules
if (! @(get-module -name ActiveDirectory).count) 
{
	import-module ActiveDirectory
}

if (! @(get-module -name Microsoft.Powershell.Host).count) 
{
	import-module Microsoft.Powershell.Host
}

if (! @(get-module -name Microsoft.WSMan.Management).count) 
{
	import-module Microsoft.WSMan.Management
}

# Variables
$TimeStamp = get-date -Format yyyyMMddHHmmss
$workingDirectory = (Resolve-Path .\).Path
$ComputerNameLower = $computername.ToLower()
$DomainLower = $domain.ToLower()
$ComputerNameUpper = $computername.ToUpper()
$DomainUpper = $domain.ToUpper()
$parentDomain = "domain.com"
[string]$strcomputer = $computername

Start-Transcript -Path $workingDirectory\AREA6_$DomainUpper.$ComputerNameUpper.$TimeStamp.txt -force

# Getting the Local Admins Group
$admins = Get-WmiObject win32_groupuser –computer "$computername.$domain.$parentDomain"
$admins = $admins | where {$_.groupcomponent –like '*"Administrators"'} 
[array]$DomainGroups = @()
[array]$IsGroup = @()

Write-Host "----------------------------------------------------------------"
Write-Host $computername "Local Administrators"
Write-Host "----------------------------------------------------------------"

# Getting Members of Local Admins
$admins | ForEach-Object {  
    $_.partcomponent –match "Win32_(.+).+Domain\=(.+)\,Name\=(.+)$" > $nul  
    $matches[2].trim('"') + "\" + $matches[3].trim('"') + " Type: " + $matches[1]
    # Finding if Local Administrator member is a Domain Group and adding it to $DomainGroups
    $String = 'Win32_Group.Domain="'+$DomainUpper+'"'
    if ($matches[0].Contains($string)){
        $DomainGroups += ($matches[3].trim('"'))
        }
    }

# Looping through all the Domain Groups in the Local Admins and getting their members
foreach ($DomainGroup in $DomainGroups){
    Write-Host "----------------------------------------------------------------"
    Write-Host "$DomainGroup"
    Write-Host "----------------------------------------------------------------"
    $Members = Get-ADGroupMember $DomainGroup -server "$DomainLower.$parentDomain"
    # If a group member is another group write it to $IsGroup so we can get them later
    foreach ($member in $members){
        If ($member.objectclass -eq "user"){
            Write-host ($member.name)" Type:"($member.objectClass)
            }
        elseif ($member.objectclass -eq "group" -And $IsGroup -notcontains $member){
            Write-Host ($member.name)" Type:"($member.objectClass)
            $IsGroup += $member
            }
        elseif($member.objectclass -eq "group" -And $IsGroup -contains $member){
            }
        }
    }
# As stated above, getting the members of nested groups
foreach ($item in $IsGroup){
    Write-Host "----------------------------------------------------------------"
    Write-Host $item.name
    Write-Host "----------------------------------------------------------------"
    $DN = $item.distinguishedName
    $objectDomain = ((($DN -replace "(.*?)DC=(.*)",'$2') -replace "DC=","") -replace ",",".")
    $members = Get-ADGroupMember $item -server $objectDomain
    foreach ($member in $members){
        write-host $member.name" Type:"$member.objectClass
        }
    }

Stop-Transcript

Limitations: This script only loops through to levels of Active Directory groups on the server. So, if you have nested groups more than that, it won’t continue deeper. I’m going to rewrite this to do full recursion in a function so you can look for that post in the future.

Adding Folder Permissions to IIS_IUSRS via Powershell

For you scripting guys that want to automate everything, For website deployments, you can modify your permissions using the NTFSSecurity Module. This is a simple way to modify ACL’s with powershell. Much easier to use the NTFSSecurity module…

First download the module from here: https://ntfssecurity.codeplex.com/

I would put it in your modules directory on the server. Then import the module with Get-Module -ListAvailable and Import-Module NTFSSecurity.

Now, on to the simple and easy code:

Add-NTFSAccess -Path 'C:\SomeFolder\SubFolder' -Account BUILTIN\IIS_IUSRS -AccessRights Read

You can also use the following AccessRights. Actually, you should be able to get any name as the parameter but these are the most common:
Modify
Read

Also, as a note, IIS_IUSRS is a special internal group that you shouldn’t/can’t prefix with the computer or domain name.

For the Network Service or IUSR, you have to use “NT AUTHORITY\NETWORK SERVICE” OR “NT AUTHORITY\IUSR”

Install / Setup Docker on Windows Server 2016

I’ve been working on installing Docker on Server 2016. Here are the steps I’ve followed and some issues I ran into:

First, you have to have Windows Server 2016.

Run Powershell as Administrator (Right click on PowerShell and RunAs Administrator) – yes, you also have to be a local administrator of the box.

Commands in order:

Install-PackageProvider containerimage -Force

Then you want to see what operating system container images are available:

This step wasn’t in the instructions I was following but is necessary and was raised on the GitHub site as well. The server will reboot after the below command is executed. After it reboots, you need to run the command again. Ugh. I thought it was done and tried to install Windows Server Core and it failed after about 30 minutes.

This is installing and then enabling the Docker Container feature on Windows

Enable-WindowsOptionalFeature -Online -FeatureName Containers

As stated above, run the command again. Once done, it will actually tell you that it’s Online and True:

Install Windows Server Core Container Image

Install the WindowsServerCore Container Image by typing the following command below. This does take a while as it downloads Windows Server Core Container Image:

Install-ContainerImage -Name WindowsServerCore

I went to have a bite to eat, took a nap, Surfed Reddit for a while, went to the bathroom and then it was done!

You can check if the image was downloaded by running looking in the directory:

ls c:\programdata\Microsoft\windows\images

The output should look like this:

 

Now we’re going to install Docker in Windows Server 2016

First, you want to download the Update-ContainerHost.ps1 powershell script. Here is the command:

Invoke-WebRequest https://aka.ms/tp5/Update-Container-Host -Outfile Update-ContainerHost.ps1

Run the command. I actually ran this to download the script while the above install of the Windows Server Core was running:

To be continued…

 

How To: Modify Trusted Hosts to connect via Powershell

We have to connect to untrusted machines via PowerShell. To do this, you have to add the machines,

First, if you have settings in there run this command to get a backup:

$trustedhosts = get-Item -Path WSMan:\localhost\Client\TrustedHosts

Then to get them to modify:

$trustedhosts.value

copy that out, add your hosts to it with comma delimited and add it with powershell:

Set-Item WSMan:\localhost\Client\TrustedHosts -Value 'Server2,Server2'

To use it in code (so it doesn’t ask you if you’re sure), you can force it by using the -Force switch

Set-Item WSMan:\localhost\Client\TrustedHosts -Value 'Server2,Server2' -Force

then check again:

get-Item -Path WSMan:\localhost\Client\TrustedHosts

PowerShell How To: Find all users where account is inactive

I was recently asked asked to find all the users in Active Directory where their account was inactive.

There is a PowerShell commandlet called Search-ADAccount that you can use to find if the account is inactive by using the parameter -AccountInactive.

This is kind of crude but works well. I couldn’t figure out how to get the headers into the csv so I simply did a write-output for the first section.

#######################
# Ed Rockwell
# Free to use
# Version 1.0
# 8/7/2017
#######################
$time = 90 # Days since last login
$users = Search-ADAccount -AccountInactive -UsersOnly -TimeSpan $time # Get all users within that timeframe with AccountInactive Property greater than $time
$path = "C:\Powershell\AccountInactive" # Where to write file

#File Name
new-item $path\users.csv -Force

# Set the header of csv (Change this if you add to the write-output below)
write-output "$("SamAccountName"),$("Enabled"),$("PasswordExpired"),$("LastLogonDate"),$("OU Location")"  | add-content -path $path"\users.csv"

# Find users 
foreach ($user in $users) 
    {
        If ($user.DistinguishedName -notmatch 'OU=Disabled Users' -and $user.DistinguishedName -notmatch 'OU=Service Accounts' -and $user.DistinguishedName -notmatch 'CN=Microsoft Exchange System Objects')
            {
                $DN = $user.distinguishedname -split ',' 
                $container = $DN[1]
                write-output "$($user.SamAccountName),$($user.Enabled),$($user.PasswordExpired),$($user.LastLogonDate),$($container)" | add-content -path $path"\users.csv"
            }
    }

How to change the Friendly Name on a certificate -Windows

I ran into the situation where someone created and applied a certificate in IIS and the friendlyName was wrong. During automatic deployments of the software, they would call into the cert store and select the certificate to use for their 443 bindings based on the friendly name. The certificate was named wrong and wouldn’t get applied during deployment or it would apply the wrong one.

Here is how to fix this using PowerShell without re-issuing the certificate.

Open up PowerShell with administrative rights and change your location to the certificate store.

We will change the certificate with the thumbprint named wrong_internal_wildcard to right_internal_wildcard

PS C:\Users\ed> set-location cert:
PS Cert:\> cd .\\localmachine\My
PS Cert:\localmachine\My> Get-ChildItem


   PSParentPath: Microsoft.PowerShell.Security\Certificate::localmachine\My

Thumbprint                                Subject
----------                                -------
EC1D0A14FA9BAD91DA24B9F87ECBCDB63E9D6F6A
E09D1799FC7F5791797EC39ED75A90345D1EE080  CN=IssuingCA, DC=domain, DC=com
A0102DDEFE92D57E8136B150F1DAEC4DA628B2AD  CN=AnotherCA, DC=domain, DC=com
8F5A004D9F831A9EA18374C3367796F6075AA578  CN=*.domain.com, O=company, L=city, S=state, C=US

PS Cert:\localmachine\My> $cert = Get-ChildItem 8F5A004D9F831A9EA18374C3367796F6075AA578
PS Cert:\localmachine\My> $cert.FriendlyName
wrong_internal_wildcard

PS Cert:\localmachine\My> $cert.FriendlyName = "right_internal_wildcard"
PS Cert:\localmachine\My> $cert.FriendlyName
right_internal_wildcard

In the above example, I have done the following:

    1. Opened Powershell

 

    1. Set-Location to the certificate store by typing Set-Location cert:

 

    1. Listed out the certs by typing Get-ChildItem

 

    1. Located the cert I wanted to change the friendly name of

 

    1. Put that cert in a variable so I could view it’s properties

 

    1. Verified that the cert is the right one by typing $cert.friendlyname

 

    1. Then changed the friendlyname by typing $cert.FriendlyName = “right_internal_wildcard”

 

    lastly, I verifed the cert friendlyname by typing $cert.FriendlyName

SSH with Powershell: Backup multiple Cisco devices

Today, I was asked to write a script to connect to all our Cisco devices and backup the configs to our fileserver. After a few hours of figuring it out, this is what I came up with

You ONLY need a READ ONLY account on the Cisco devices.

We run this every day at 2:00 AM and backup many Cisco devices.

I downloaded and use this SSH.NET Library: SSH With Powershell

You have to move it to your powershell modules directory and then import-module

##################################
# Ed Rockwell
# Requires SSH Module from here:
# http://www.powershelladmin.com/wiki/SSH_from_PowerShell_using_the_SSH.NET_library
# When downloading the above, you need to right click the download and unblock it in the properties
# Then add the contents to your powershell modules
# Free to distribute! Keep: http://www.EdRockwell.com
# 5/15/2014
 
Import-Module SSH-Sessions
 
$FileServerLocation = '\\path\to\file\server\'
$DeviceList = Get-Content '\\path\to\text\file\of\devices\NetworkDeviceList.txt'
$Date = (Get-Date -f yyyyMMdd)
 
md $Date
cd $Date
 
foreach ($Device in $DeviceList)
{
New-SSHSession -ComputerName $Device -Username YourCiscoUser -Password Password4CiscoUser
$DeviceBackup = Invoke-SshCommand -ComputerName $Device -Command 'show start'
$DeviceBackup | out-file $device'.txt'
}
 
cd ..
Move-Item $Date $FileServerLocation

How To: Import AD Users from .csv file

I had a project that required me to make over 40 domain accounts. I decided that it was time to create all the domain accounts with a Powershell script. The script I came up with uses an import csv file with all the accounts and info I needed in it. Make sure you take the time to plan a naming convention for your AD accounts. In this case, they were a type of service account for many different environments. To keep it quick, I decided to not auto-gen the passwords so I simply put them in the csv file and removed them when I was done. Well, it took me a day or so to figure out my script to create ad accounts because I had problems…

My troubleshooting was a bit flawed but I didn’t know it until the very end (After running for all the users). I was having problems with the script ending in error . This is a very generic error. I knew that my accounts had spaces in them for $GivenName $Surname and $Name. So, I went through the trouble making sure that my variable properties with spaces had “” around them. Yes, that’s a pair of double quotes. My Display name I wanted to use had GivenName and Surname in it with a space. So it looks like this: Displayname = ($User.”GivenName”+” “+$User.”Surname”). The quotes around “GivenName” allowed me to use two names in the GivenName column of the .csv file and the same for “Surname”. This way I can create an account that looks like: First Second Third Forth. In other words, my “GivenName” in my CSV was First Second so I had to put “” around it in the script so it would read it as one word. What I missed is after everything was put together, the fields were over 20 characters. Well, the limit on Windows 2008 Account “Names” is 20 characters. Until I ran my script and found that it didn’t create about 1/2 of them, I started analyzing the .csv file to figure out why it didn’t work. I found out that the ones that didn’t get created are the ones that were over 20 characters.

Here is the script. You will notice that I’ve got a comment in the script for the .csv file’s header fields. You can add to them or remove as needed. I think it’s easier to view the powershell references on Microsoft’s site. Here is the link to Set-ADUser cmdlet: http://technet.microsoft.com/en-us/library/ee617215.aspx. For each property, you need it in the script and in the .csv file. If there are special characters or spaces. Remember to use the “” around it in the script. Also, make sure you are not exceeding the field length in AD for each property. The sAMAccountName (pre-Windows 2000 logon name) is limited to 20 characters for user objects. This is what got me a few times 🙁

Let me know if you need anything below explained! I’ll answer all comments on this the same day if I can.

# REQUIRE DA ACCOUNT  
if (! ($ENV:USERNAME).ToUpper().EndsWith("ADM"))
{
	throw "SCRIPT MUST BE RUN WITH ADMIN ACCOUNT"
}
 
# IMPORTING AD MODULE
if (! @(get-module -name ActiveDirectory).count) 
{
	import-module ActiveDirectory
}
 
# GETTING USERS FROM CSV FILE
 
### NOTE: The Account Column CAN NOT be more than 20 characters or it will fail on them!
 
$Users = Import-CSV C:\CreateADUsers.csv 
# columns are: GivenName,Surname,Name,Account,Password,Department,Description
 
 
# CREATING USERS
# If you don't have two word attributes, you can remove some of the "" below after the $User.
foreach($User in $Users)
{
	$Params = @{
		SamAccountName = $User.Account
		Name = $User."Name"
		GivenName = $User."GivenName"
		Surname = $User."Surname"
		Displayname = ($User."GivenName"+" "+$User."Surname")
		UserPrincipalName = ($User.Account+"@domain.com")
		Department = $User."Department"
		Description = $User."Description"
		Path = "OU=Your OU,DC=domain,DC=com"
		PasswordNeverExpires = $true
		AccountPassword = (ConvertTo-SecureString $User.Password -AsPlainText -Force)
		Enabled = $true
	}
	new-ADUser @Params
}

I’m a seasoned Systems Administrator with experience starting in the early 90’s when 286 computers with 20 and 30 Mhz processors running Windows 3.1 which was the newest operating system.

…and that’s the way Ed does it 🙂 — Thanks Scott J. for that 🙂

Connect remotely to a computer with powershell and get ipconfig /all from it

Are you looking to connect remotely to a computer and run powershell commands. Below is a simple one liner that will connect to a computer and get the ipconfig /all and display it on your computer.

If it doesn’t work, you may have to enable WinRM on the remote computer. This can be done with group policy or simply typing Winrm quickconfig at the command prompt on the computer you want to connect to. This has to be done before hand so there may be some prep involved but when you have Winrm enabled on all your computers, you can connect with powershell and run all sorts of commands.

Here is

Invoke-Command -Computer <EnterComputerNameHere> -ScriptBlock {ipconfig /all}