Tag Archives: Powershell

Get file size of old files on a fileserver

So I was at a costumer. They wanted an export of the file size that files older than 7 years populate on the file server. I made a very simple script that does the job.

$Locatie = "D:\"
#Verander het cijfer om het aantal jaar aan te passen
$Datum = (Get-Date).AddYears(-7)
$Outfile = ".\Output.txt"
$List = @(GCI $Locatie -Recurse | Where {$_.LastWriteTime -lt $Datum})
$Outinfo = "De lijst bevat "+ $list.count +" bestanden"
Add-Content -Path $Outfile -Value $Outinfo
$Bytes = 0
ForEach ($Item in $List){($Bytes=$Bytes + [int]$Item.length)}
$Outinfo = "De Bestanden bevatten $Bytes bytes"
Add-Content -Path $Outfile -Value $Outinfo
$Outinfo = "Dat is "+ $Bytes/1048576 +" MegaBytes"
Add-Content -Path $Outfile -Value $Outinfo

App-v 5 and Citrix Provisioning

An administrator here came to me and asked me about how to Powershell around a problem with the App-V 5.0 Client Cache directory. The implementation configured is that App-V uses a folder on the D disk. This disk is dedicated. It is not on the distributed Vdisk. When the server reboots the cache becomes corrupted and the App-V service does not start. A sync is necessary in order to start the App-V 5.0 service. I made a script that clears a few reg values, deletes the App-v Client Connection Groups, App-V Client Packages, the AppV folder in Programdata and the entire folder on the D disk.

I handed over the script to the administrator and asked him to create a scheduled task that run at system startup. The reason for  running the script this way, is that creating a GPO that run a Powershell script will not statisfy the required security context. The SYSTEM account doesn’t have enought permissions to complete the script. PSRemoting will not work either. When running the script on another server with the Invoke-Command -Scriptblock {} fails. This is probably due to a bug in App-V. The account has enough rights and UAC is disabled. Yet all attempts fail. Please review the script before implementing

 

<#Script Variables#>
$ServiceName = "AppVClient"
$ProgramDataAppV = "C:\ProgramData\Microsoft\AppV"
$AppVData = "D:\App-V 5.0"

<#Regvalues#>
$Regvalues=@()
$Regvalues +=@{Regvalue="HKLM:\SOFTWARE\Microsoft\AppV\Client\PackageGroups"}
$Regvalues +=@{Regvalue="HKLM:\SOFTWARE\Microsoft\AppV\Client\Packages"}
$Regvalues +=@{Regvalue="HKLM:\SOFTWARE\Microsoft\AppV\Client\Streaming\Packages"}

#Service stoppen om Program Data te verwijderen.
$State = Get-Service $ServiceName
If ($State.status -NotMatch "Stopped"){Stop-Service $ServiceName
Start-Sleep -s 5}
Remove-Item $ProgramDataAppV -force -recurse

#Verwijderen van de Registersleutels
Foreach ($item in $Regvalues){If ((Test-Path $item.Regvalue) -ne $True){Remove-item $item.regvalue -force -Recurse}}

#Service Starten om initiele synch uit te voeren zodat de D schijf wordt vrijgegeven.
$State = Get-Service $ServiceName
If ($State.status -Match "Stopped"){Start-Service $ServiceName
Start-Sleep -s 5}
Get-AppvPublishingServer | Sync-AppvPublishingServer

#Service Stoppen om Packages en AppvClientConnectionGroup te verwijderen.
$State = Get-Service $ServiceName
If ($State.status -NotMatch "Stopped"){
Write-Host Stoppen
Stop-Service $ServiceName
Start-Sleep -s 5}
Get-AppvClientConnectionGroup -all | Remove-AppvClientConnectionGroup
Get-AppvClientPackage -all | Remove-AppvClientPackage
Start-Sleep -s 10

#Service stoppen om App-v folder op de D schijf te verwijderen.
If ($State.status -NotMatch "Stopped"){Stop-Service $ServiceName
Start-Sleep -s 5}
Remove-Item $AppVData -Force -Recurse
Start-Sleep -s 10

#Check of de folder ook daadwerkelijk verwijderd is.
If ((Test-Path $AppVData) -eq $True){

#Proberen de D schijf leeg te krijgen door eerst te synchen
Get-AppvPublishingServer | Sync-AppvPublishingServer
Start-Sleep -s 5
$State = Get-Service $ServiceName
If ($State.status -NotMatch "Stopped"){Stop-Service $ServiceName
Start-Sleep -s 5}
Get-AppvClientConnectionGroup -all | Remove-AppvClientConnectionGroup
Get-AppvClientPackage -all | Remove-AppvClientPackage
Start-Sleep -s 10
Remove-Item $AppVData -Force -Recurse

#Laatste check op de D schijf om te kijken of de folder nu daadwerkelijk weg is.
If ((Test-Path $AppVData) -eq $True){
$Outinfo = "De App-V Data op de D Schijf is na twee pogingen niet succesvol verwijderd. Bekijk het logboek."
Add-Content -Value $Outinfo -Path "C:\ScriptFout.txt"
$Outinfo = Get-Date
Add-Content -Value $Outinfo -Path "C:\ScriptFout.txt"
}}

#Laatste Sync uitvoeren
Get-AppvPublishingServer | Sync-AppvPublishingServer

Renaming files and moving it to a different location

An OCE printer can only scan files to a FTP location with one name. There are no unique attributes that the Multifuctional printer can add to a file. This results in files being overwritten if not renamed immediately after being transferred. I’ve written a little script that is run every 5 seconds via Task Scheduler. This script renames files in a  unique format and then moves the file to some place else.

$Folder = "C:\Inetpub\FTProot\"
$Destination = "\\Ergensopdebozewereld"
$Applytime = (Get-Date).AddSeconds(-20)
$Content = @(gci $Folder | where {$_.LastWriteTime -lt $Applytime -and $_.PSIsContainer -eq $False})
Foreach ($item in $Content){$TEMP = $Item.LastWriteTime
$Time = Get-Date $TEMP -Format yyyyMMdd_HHmmss
$Name = $item.name.replace(".tif","")
Rename-Item -Path $Item.fullname -NewName "$Name_$Time.tif"
$Item = GI "$Folder\$Name_$Time.tif"
Move-Item -Path $Item -Destination $Destination -Force}

Remove old definitions

Symantec Endpoint Protection has a tendency to preserve definitions that are no longer needed. I’ve written a powershell script in three variations.

First is a powershell script that deletes any folders in the Virusdef above the count of 3. So if there are 4 folders with definitions it will delete the oldest.

$Servers =@( Import-CSV ".\Server.csv")
Foreach ($Server in $Servers){$Folders =@( gci "\\$Server\C$\Program Files\Common Files\Symantec Shared\VirusDefs" | Where {$_.PsIsContainer -and $_.Name -notmatch "Binhub" -and $_.Name -notmatch "Texthub"} | Sort CreationTime -Descending | Select -Skip 3})
ForEach ($Folder in $Folders){Remove-Item $Folder -Force -Recurse}

Second is a powershell script that deletes any folder in de Virusdef folder older than 2 days ago. Of course only definition folders not the other two folders.

$Datum = (Get-Date).AddDays(-2)
$Servers = @(Import-CSV ".\Server.csv")
Foreach ($Server in $Servers){gci -Path "\\$Server\C$\Program Files\Common Files\Symantec Shared\VirusDefs" | Where-Object { $_.PSIsContainer -and $_.CreationTime -lt $Datum -and $_.Name -notmatch "Binhub" -and $_.Name -notmatch "Texthub" } | Remove-Item -Force}

The last script is a combination of the above. It will any folder older than two days ago skipping the first. If a server hasn’t updated its definition it will not delete the active  definition.

$Datum = (Get-Date).AddDays(-2)
$Servers = @(Import-CSV ".\Server.csv")
Foreach ($Server in $Servers){gci -Path "\\$Server\C$\Program Files\Common Files\Symantec Shared\VirusDefs" -Recurse -Force | Where-Object { $_.PSIsContainer -and $_.CreationTime -lt $Datum } | Sort CreationTime -Descending | Select -Skip 1| Remove-Item -Force}

All Script require a script.csv file with servers. Just a header with Server. Neighter do you need any delimiters. Just a list with servers

You can replace import-csv with get-content as needed.

Replace text in local TNSNames.ora with Powershell

An administrator came to me today. Recently they had migrated from Oracle 10 to Oracle 11 and from HP UX to ODA (Oracle Database Appliance). Due to the fact that in this organization there are over 2000 databases, some applications wheren’t tested. In some cases application server look at the wrong TNSNames.ora. The request was to replace the text after host for the approriate host. I had the ‘luck’ that this request only included servers.

I created a that resolve his problem. The script contacts the ActiveDirectory for the list of “servers”. Then the script test if that server has an Oracle directory under C:\. Change this if you have Oracle installed under a different folder. Next there’s a check under the Oracle Directory for any folder that match Ora. These folders are included in an array. The next thing that will be checked is, if the TNSNames.ora exists in the usual folder. If that is the case the script continues. If it doesn’t exist the script will continue with the next folder or server.

Then the content of the TNSNames.ora will be loaded into the memory. The script renames the original TNSNames.ora to TNSNames.old. Or if that file is already in use in some other extension. Then all lines in the TNSNames will be check on the existance of few specific words and those words will be replaced with something else. At last the array NewTNSNames will be written to the location.

 

Import-Module ActiveDirectory
$ADComputers = Get-ADComputer -Filter * -Properties * | Where {$_.OperatingSystem -match "Server"}
$Computers =@()
ForEach ($ADComputer in $ADComputers){$ADComputer = $ADComputer.DNSHostName
If((Test-Path "\\$ADComputer\C$\Oracle") -eq $True){$Computers += $ADComputer}}
ForEach ($Computer in $Computers){$Folders =@( gci "\\$Computer\c$\Oracle\Ora*")
ForEach ($Folder in $Folders){If ((Test-Path "$Folder\Network\Admin\TNSNames.ora") -eq $True){$TNSNames = get-content "$Folder\Network\Admin\TNSNames.ora"
If ((Test-Path "$Folder\Network\Admin\TNSNames.old") -eq $False){Rename-item -Path "$Folder\Network\Admin\TNSNames.ora" -NewName "$Folder\Network\Admin\TNSNames.old"}
If ((Test-Path "$Folder\Network\Admin\TNSNames.NaChangeAugustus2014") -eq $False){Rename-item -Path "$Folder\Network\Admin\TNSNames.ora" -NewName "$Folder\Network\Admin\TNSNames.NaChangeAugustus2014"}
$NewTNSNames =@()
ForEach ($Item in $TNSNames){
If ($Item -match "odatst101a"){$NewTNSNames += $Item.replace("odatst101a","RobIsDeBarbecueKoning.local")}
If ($Item -match "odatst101b"){$NewTNSNames += $Item.replace("odatst101b","RobIsDeBarbecueKeizer.local")}
If ($Item -notmatch "odatst101a" -and $Item -notmatch "odatst101b"){$NewTNSNames +=  $Item}}
ForEach ($Item in $NewTNSNames){Add-Content -Value $Item -Path "$Folder\Network\Admin\TNSNames.ora" }}}}

Registry permissions of users by mistake overwritten

An administrator applied an GPO which overwrites the permissions on the registry of users by mistake.  This results in users who cannot login after they lost permissions on their ntuser.dat registry hive.

We had an immediate problem with the users so decided to restore the entire terminal profiles share. However you can choose to solve the problem. I’ve written a script really quite to at least know how big the problem really is.

We have PSremoting enabled on all terminal servers. Which gave us the posibility to run the script on the entire environment.

Import-Module ActiveDirectory
$Computers = Get-ADComputer -Filter * | Where {$_.Name -match "TERMINALSERVERPREFIX"}

Foreach ($Computer in $Computers){

Enter-Pssession $Computer
 
New-PSDrive -PSProvider Registry -Name HKU -Root HKEY_USERS
 Set-Location HKU:
 $list = GCI
 $Hostname = Hostname
 $Export = "C:\Permissions"+"_"+"$Hostname"+".csv"
 Foreach ($item in $list){$ACLs = Get-ACL $Item.PSChildName | Foreach-Object { $_.Access }
 Foreach ($ACL in $ACLs){
 If ($ACL.IdentityReference -match "ADGROUP Or User"){$Value = $Item.Name + ";" + $ACL.IdentityReference + ";" + $ACL.AccessControlType + ";" + $ACL.IsInherited + ";" + $ACL.InheritanceFlags + ";" + $ACL.PropagationFlags + ";" + $ACL.FileSystemRights
 Add-Content -Value $Value -Path $Export}}}
 Set-Location C:
 Copy-Item -Path $Export -Destination "\\Shared Location"

}

This writes back the SID on which an Active Directory Group had permissions on. You can of course use the information given but you have the compare the SID’s with your Active Directory SID.

Exporting the SID’s of your AD can be done by running the following code:

Import-Module ActiveDirectory
$Export = "C:\SIDS.csv"
$List = Get-ADUser -filter * | Select Name,SID
$Header = "Name;SID"
Add-Content -Value $Header -Path $Export
Foreach ($Item in $list){
$Outinfo = $item.Name + ";" + $Item.SID
Add-Content -Value $Outinfo -Path $Export}

Or

Import-Module ActiveDirectory
$Export = "C:\SIDS.csv"
$List = Get-ADUser -filter * | Select Name,SID
$list | Export-CSV -Path $Export -Delimiter ";" -NoTypeInformation 

Sign your scripts

In some high security environments is it required to sign you scripts. To do so you need a code sign certificate. One of the cheapest around is the one of StartSSL costing you only $ 59,99 or roughly € 40 ~ € 45.

I requested a code sign certificate via their site. The advantage of signing scripts with a public certificate is obviously that the script is trusted and you won’t be prompted to approve the script.

To sign a script you need to have the code signing certificate installed and also you need the function Set-AuthenticodeSignature. It is highly recommended that you use a timestamp server. This will ensure that the code is signed when the certificate was valid. So if you certificate expired you won’t have to sign all you code again. StartSSL has a Timestamp Server, but you can use any timestamp server that you prefer. I were at the moment not able to sign my scripts using the StartSSL timestamp server. I use Globalsign at the moment. The certificate from this timestamp server is valid until 2024. If anyone uses your scripts by then, you’d done your job very well ;-).

$Cert=(dir cert:currentuser\my\ -CodeSigningCert)
Set-AuthenticodeSignature ".\Script.ps1" $Cert -IncludeChain All -TimestampServer "http://timestamp.globalsign.com/scripts/timstamp.dll"

Including the chain will ensure that any intermediate certificate authority will be trusted. Be sure to save you code in ANSI format and not the default Unicode:BigEndian that Powershell ISE uses. Notepad(++) will save in ANSI by default. You can use the regular Unicode if you have any diacritic stated.

Your script will be appended with some additional code with certificate information. Note that Powershell v3 will need SHA256 algorithm. Powershell v2 (if anyone still usses that) accepts scripts which are sign with anything up to SHA1. You can specify the algorithm you want to use by entering -HashAlgorithm SHA512. By default in v3 and up it is SHA256.

Manage websites from local server

$list =@(Get-WmiObject -Namespace "root/Webadministration" -Query 'Select * from Site')
Foreach ($item in $list){write-host $item.name}

IIS6 compatiblity uses a different namespace root/MicrosoftIISv2

All you need to do is get cred en authenticate to a different server using either -ComputerName $Hostname or  -Authentication “6”.

Next you can create a remote job using the earlier cached credentials and create a script with the listed names like this:

Import-Module WebAdministration
Stop-Website "$name" 
$State = Get-website |Where {$_.Name -eq "$Name"} | Select State
If ($State -notmatch "Stopped"){Stop-Website $Name }

Of course you can also you the schtasks /create command (in Powershell) to add a job that deletes itself or enable-psremoting.

An entire different approach would be to connect using the servermanager via Powershell. You can load the servermanager and connect to a server. The advantage would be that one server is responsible. You do need access to that server. Some high secure environments block this kind of traffic. You can load any dll for IIS Web Administration

[System.Reflection.Assembly]::LoadFrom( "C:\windows\system32\inetsrv\Microsoft.Web.Administration.dll" )
$computer = "COMPUTERNAME"
$ServerManager = [Microsoft.Web.Administration.ServerManager]::OpenRemote($computer.ToLower())
[int]$ID="1"
Foreach ($item in $ServerManager.sites){
Write-host "$ID $item"
$ID++
}

while ($ID -gt $Servermanager.sites.count){
     $ID = read-host "Voer de site die u wilt stoppen"
      $ID--}


$ServerManager.Sites.Item($ID).stop()