I configure a custom PowerShell Task to analyze the Code Coverage of Azure DevOps repository. The steps are:
Search for specific test assemblies (*Test.dll)
Run coverlet and pass the test assemblies
Proof $LASTEXITCODE is not equal 2 (coverage lower than a threshold)
If $LASTEXITCODE is equal 2
Run ReportGenerator with coverlet cobertura summary
Send e-mail with reports to last committer (get from git git --git-dir=$git log -1 --format="%ae")
The Problem I have:
The Commiter is not interested in the code coverage of the whole repository, he wants to know the coverage of his commit.
What I try to achieve:
How can I check up whether a commit contains a test assembly or not? I want to analyze only the test assemblies of the last commit.
If there is no test assembly: do nothing
If there are test assemblies: analyze only this specific one and inform the developer about his code coverage
PowerShell Script:
param([string]$Root, [int]$Threshold = 80, [string]$FromMail, [string]$Output = "Report", [string[]]$Include = #("*Tests.dll"), [string[]]$Exclude)
#VARIABLES
$format = "cobertura" #FORMAT OF THE GENERATED COVERAGE REPORT (json [default]/lcov/opencover/cobertura/teamcity)
$thresholdType = "line" #COVERAGE TYPE TO APPLY THE THRESHOLD TO (line/branch/method)
$coverletOutput = "cobertura.xml" #OUTPUT OF THE GENERATED COVERAGE REPORT
$reportTypes = "HtmlInline_AzurePipelines;Cobertura" #THE OUTPUT FORMATS AND SCOPE (SEPARATED BY SEMICOLON) (Badges/Cobertura/CsvSummary/Html/HtmlChart/HtmlInline/HtmlInline_AzurePipelines/HtmlInline_AzurePipelines_Dark/HtmlSummary/Latex/LatexSummary/MHtml/PngChart/SonarQube/TeamCitySummary/TextSummary/Xml/XmlSummary)
#CODE COVERAGE SCRIPT
#-----------------------------------------------------------------------------------------------------#
##The script should analyze the code coverage of a test assembly and create a `.xml` report.
##Requeried tools: [coverlet](https://github.com/tonerdo/coverlet/blob/master/Documentation/GlobalTool.md), [ReportGenerator](https://automationrhapsody.com/code-coverage-manual-automated-tests-opencover-net-applications/), [git](https://git-scm.com/downloads)
##Root = is the directory where the script seeks recursively for files with `$Include` patterns
##Threshold = is the threshold of the code coverage that will accept the test
##FromMail = is the mail address from which the script should send the coverage warning
##Output = is the output path of the `.xml` report file
##Include = is a pattern list for the recursive search of test assemblies which should be included for the code coverage analysis (for instance `#("*Tests.dll", "*Unit.dll")`)
##Exclude = is a pattern list of subdirectories which should be excluded for the code coverage analysis (for instance `#("*\obj\*", "*\Release\*")`)
#-----------------------------------------------------------------------------------------------------#
#JOIN INCLUDE & EXCLUDE FOR PRINTS
$includeJoin = $($Include -join "', '")
$excludeJoin = $($Exclude -join "', '")
Write-Host "Root:`t`t$Root`nThreshold:`t$Threshold`nFromMail:`t$FromMail`nOutput:`t$Output`nInclude:`t'$includeJoin'`nExclude:`t'$excludeJoin'"
#CHECK ARGUMENTS
if ($Root -eq "" -or $Threshold -lt 0 -or $FromMail -eq "" -or $Output -eq "" -or $null -eq $Include) {
Write-Host "##vso[task.logissue type=error;][ps1] error: missing root directory, coverage threshold, output directory or include pattern list of unit test .dll," -ForegroundColor Red
exit(-1)
}
if ($null -eq $Exclude) { $Exclude = #() }
#CHECK VALID E-MAIL
try { $_ = new-object net.mail.mailaddress($FromMail) }
catch { Write-Host "##vso[task.logissue type=error;][ps1] error: invalid mail address '$FromMail'" -ForegroundColor Red; exit(-1) }
#CHECK COMMANDS
[string[]] $cmds = "coverlet", "reportgenerator", "git"
foreach ($cmd in $cmds) {
if (Get-Command $cmd -errorAction SilentlyContinue) { Write-Host "[$cmd] path: '$($(Get-Command $cmd).Path)'" -ForegroundColor Green }
else { Write-Host "##vso[task.logissue type=error;][$cmd] error: '$cmd' command not exist" -ForegroundColor Red; exit(-1) }
}
#SET $PWD
Set-Location -Path $Root
#FIND GIT REPOSITORY (FOR COMMIT & E-MAIL)
$git = Get-ChildItem $pwd -Include ".git" -Recurse -Directory -Force -ErrorAction SilentlyContinue | Select-Object -First 1
if ($null -eq $git) { Write-Host "##vso[task.logissue type=error;][git] error: missing repository in directory '$($pwd.Path)' and his subdirectories" -ForegroundColor Red; exit(-1) }
#SEARCH FOR $INCLUDE FILES IN $ROOT
Write-Host "[ps1] search directory: '$Root'" -ForegroundColor Yellow
Write-Host "[ps1] search include: '$includeJoin'" -ForegroundColor Yellow
$files = Get-ChildItem -Path $Root -Include $Include -Recurse -File -Name -ErrorAction SilentlyContinue
#SEARCH FOR $EXCLUDE IN $FILES
$Exclude | Where-Object { $ex = $_; $files = $files | Where-Object { $_ -notlike $ex } }
Write-Host "[ps1] search exclude: '$excludeJoin'" -ForegroundColor Yellow
Write-Host "[ps1] search results:" -ForegroundColor Yellow
$files | Where-Object { Write-Host "`t-$_" -ForegroundColor Gray }
#CHECK FILES FOUND
if ($files.Count -eq 0) { Write-Host "##vso[task.logissue type=error;][ps1] error: error: no files with include pattern '$includeJoin' found in '$Root'" -ForegroundColor Red; exit(-1) }
#START COVERLET
foreach ($file in $files) {
Write-Host "[coverlet] analyse: '$file'" -ForegroundColor Yellow
$path = '"{0}"' -f $file
coverlet $path --target "dotnet" --targetargs "vstest $path --logger:trx" --format $format --threshold $Threshold --threshold-type $thresholdType --output $coverletOutput
$exitCoverlet = $LASTEXITCODE
Write-Host "[coverlet] exit code for '$file': $exitCoverlet" -ForegroundColor Yellow
if ($exitCoverlet -ne 0) { break }
}
#COVERAGE IS TO LOW (2)
if ($exitCoverlet -eq 2) {
#START REPORT GENERATOR
reportgenerator -reports:$coverletOutput -reporttypes:$reportTypes -targetdir:$('"{0}"' -f $Output)
$exitReportGenerator = $LASTEXITCODE
Write-Host "[reportgenerator] exit code: $exitReportGenerator" -ForegroundColor Yellow
#SEND MAIL
$from = $FromMail
$to = git --git-dir=$git log -1 --format="%ae"
$attachments = Get-ChildItem -Path "$Output" -Filter *.htm -Recurse | ForEach-Object { $_.FullName }
$index = Get-ChildItem -Path "$Output" -Filter index.htm -Recurse | ForEach-Object { $_.FullName }
$commit = git --git-dir=$git log -p $git -1 --pretty=%B
$subject = "Code Coverage in Commit '$commit'"
$body = "The code coverage of your commit '$commit' is under the threshold of $Threshold %.<br>Show attachments for more details.<br><br>" + $(Get-Content $index)
$smtpServer = "smtp.server.de"
$smtpPort = "25"
Write-Output "##vso[task.logissue type=warning;][ps1] code coverage is to low, send mail to: $to"
Send-MailMessage -From $from -to $to -Subject $subject -Body $body -BodyAsHtml -SmtpServer $smtpServer -port $smtpPort -Attachments $attachments
}
Azure DevOps Server Version: 17.143.28912.1 (AzureDevOps2019.0.1).
Agent: Self-Hosted Agent vsts-agent-win-x64-2.144.2.
EDIT: Code Coverage of commited Test-Assemblies
I modifie my first script with the following snippet steps:
read out all changed files from last git commit
search all project files (*csproj, *vbproj) with filter *Test*
check project file includes changed file
remove .proj extension, replace it with .dll
create assembly path with a given $OutputAssembly (bin\Release) from user
Snipped:
#GET THE LAST COMMITED FILES
$commitedFiles = git --git-dir=$GitPath diff-tree --no-commit-id --name-only -r $lastCommit
#SEARCH FOR PROJECT FILES IN $PWD WITH FILTER
$Filter = "*Test*"
$projs = Get-ChildItem -Path $pwd -Recurse -Filter $Filter -Include #("*csproj", "*vbproj")
#SEARCH FOR $EXCLUDE IN $FILES
$Exclude = #("*\obj\*")
$Exclude | Where-Object { $ex = $_; $projs = $projs | Where-Object { $_ -notlike $ex } }
Write-Host "[ps1] search exclude: '$excludeJoin'" -ForegroundColor Yellow
Write-Host "[ps1] search results:" -ForegroundColor Yellow
$projs | Where-Object { Write-Host "`t-$_" -ForegroundColor Gray }
#CHECK PROJECT FILES FOUND
if ($projs.Count -eq 0) { Write-Host "##vso[task.logissue type=error;][ps1] error: error: no projects with filter '$Filter' and include pattern '$includeJoin' found in '$Root'" -ForegroundColor Red; exit(-1) }
#ASSEMBLIES LIST
$assemblies = #()
#LOOP ALL .PROJ FILES
foreach ( $proj in $projs ) {
#LOOP ALL LINES IN .PROJ FILE
foreach ( $line in (Get-Content $proj) ) {
if ( $line -match 'Compile\s+Include="([^"]+)"' ) {
#COMPILED FILE IN .PROJ
$file = Split-Path $matches[1] -Leaf
#LOOP ALL COMMITED FILES
foreach($commitedFile in $commitedFiles){
#GET FILE NAME
$name = Split-Path $commitedFile -Leaf
#ADD ASSEMBLY BASED ON .PROJ BASENAME
if($name -eq $file) { $assemblies += $proj.BaseName + ".dll" }
}
}
}
}
#FEEDBACK CHANGED ASSEMBLIES
Write-Host "[ps1] changed assemblies:" -ForegroundColor Yellow
$assemblies | Where-Object { Write-Host "`t-$_" -ForegroundColor Gray }
#LOOP ALL ASSEMBLIES
$OutputAssembly = "bin\Release"
foreach ($assembly in $assemblies){
$path = [IO.Path]::Combine($Root , $OutputAssembly, $assembly)
#CHECK ASSEMBLY PATH
if (-not (Test-Path -Path $path)) {
Write-Host "##vso[task.logissue type=warning;][ps1] warning: missing assembly '$assembly' at: '$path'" -ForegroundColor Yellow;
}
else {
#START COVERLET
}
}
If I understand the question well, you can do something like this:
# Get the last commit SHA1
$lastCommit = "$(Build.SourceVersion)"
# Get the last commit files
$files = git diff-tree --no-commit-id --name-only -r $lastCommit
if($files -match "Test.cs")
{
# Do something...
}
else
{
# Do something else...
}
Because usually if you have Test.dll so the source code should be Test.cs.
Related
I am using a script I modified slightly to backup user library files. For some reason it can backup all libraries but fails to backup any of the Desktop files.
I've tried running this as various users and on different machines but with the same result.
This is in the latest Powershell version on Windows 10. Perhaps the code has changed since Windows 7? Thanks in advance for any help.
Set-StrictMode -Off
#create directories for backup if needed
$TARGETDIR1 = "c:\temp"
if(!(Test-Path -Path $TARGETDIR1 )){
New-Item -ItemType directory -Path $TARGETDIR1
}
$TARGETDIR2 = "c:\temp\backup"
if(!(Test-Path -Path $TARGETDIR2 )){
New-Item -ItemType directory -Path $TARGETDIR2
}
$TARGETDIR3 = "c:\temp\backup\Download"
if(!(Test-Path -Path $TARGETDIR3 )){
New-Item -ItemType directory -Path $TARGETDIR3
}
$TARGETDIR4 = "c:\temp\backup\Staging"
if(!(Test-Path -Path $TARGETDIR4 )){
New-Item -ItemType directory -Path $TARGETDIR4
}
#Variables, only Change here
$Destination="c:\temp\backup" #Copy the Files to this Location
$Destination="C:\temp\backup\Download"
$Staging="C:\temp\backup\Staging"
$ClearStaging=$true # When $true, Staging Dir will be cleared
$Versions="5" #How many of the last Backups you want to keep
$BackupDirs="$env:USERPROFILE\Desktop", "$env:USERPROFILE\Documents", "$env:USERPROFILE\Downloads", "$env:USERPROFILE\Favorites", "$env:USERPROFILE\Pictures", "$env:USERPROFILE\Videos", "$env:USERPROFILE\OneDrive", "$env:USERPROFILE\Links"#What Folders you want to backup
#commented out for now --tom
$ExcludeDirs="C:\Users\seimi\OneDrive - Seidl Michael\0-Temp\Dir1","C:\Users\seimi\OneDrive - Seidl Michael\0-Temp\Dir2" #This list of Directories will not be copied
$LogName="Log.txt" #Log Name
$LoggingLevel="3" #LoggingLevel only for Output in Powershell Window, 1=smart, 3=Heavy
$Zip=$false #Zip the Backup Destination
$Use7ZIP=$false #Make sure it is installed
$RemoveBackupDestination=$false #Remove copied files after Zip, only if $Zip is true
$UseStaging=$true #only if you use ZIP, than we copy file to Staging, zip it and copy the ZIP to destination, like Staging, and to save NetworkBandwith
#Send Mail Settings
# $SendEmail = $false # = $true if you want to enable send report to e-mail (SMTP send)
# $EmailTo = 'test#domain.com' #user#domain.something (for multiple users use "User01 <user01#example.com>" ,"User02 <user02#example.com>" )
# $EmailFrom = 'from#domain.com' #matthew#domain
# $EmailSMTP = 'smtp.domain.com' #smtp server adress, DNS hostname.
#STOP-no changes from here
#STOP-no changes from here
#Settings - do not change anything from here
$ExcludeString=""
#[string[]]$excludedArray = $ExcludeDirs -split ","
foreach ($Entry in $ExcludeDirs)
{
$Temp="^"+$Entry.Replace("\","\\")
$ExcludeString+=$Temp+"|"
}
$ExcludeString=$ExcludeString.Substring(0,$ExcludeString.Length-1)
#$ExcludeString
[RegEx]$exclude = $ExcludeString
if ($UseStaging -and $Zip)
{
#Logging "INFO" "Use Temp Backup Dir"
$Backupdir=$Staging +"\Backup-"+ (Get-Date -format yyyy-MM-dd)+"-"+(Get-Random -Maximum 100000)+"\"
}
else
{
#Logging "INFO" "Use orig Backup Dir"
$Backupdir=$Destination +"\Backup-"+ (Get-Date -format yyyy-MM-dd)+"-"+(Get-Random -Maximum 100000)+"\"
}
#$BackupdirTemp=$Temp +"\Backup-"+ (Get-Date -format yyyy-MM-dd)+"-"+(Get-Random -Maximum 100000)+"\"
$Log=$Backupdir+$LogName
$Log
$Items=0
$Count=0
$ErrorCount=0
$StartDate=Get-Date #-format dd.MM.yyyy-HH:mm:ss
#FUNCTION
#Logging
Function Logging ($State, $Message) {
$Datum=Get-Date -format dd.MM.yyyy-HH:mm:ss
if (!(Test-Path -Path $Log)) {
New-Item -Path $Log -ItemType File | Out-Null
}
$Text="$Datum - $State"+":"+" $Message"
if ($LoggingLevel -eq "1" -and $Message -notmatch "was copied") {Write-Host $Text}
elseif ($LoggingLevel -eq "3") {Write-Host $Text}
add-Content -Path $Log -Value $Text
}
#Create Backupdir
Function Create-Backupdir {
New-Item -Path $Backupdir -ItemType Directory | Out-Null
sleep -Seconds 5
Logging "INFO" "Create Backupdir $Backupdir"
}
#Delete Backupdir
Function Delete-Backupdir {
$Folder=Get-ChildItem $Destination | where {$_.Attributes -eq "Directory"} | Sort-Object -Property CreationTime -Descending:$false | Select-Object -First 1
Logging "INFO" "Remove Dir: $Folder"
$Folder.FullName | Remove-Item -Recurse -Force
}
#Delete Zip
Function Delete-Zip {
$Zip=Get-ChildItem $Destination | where {$_.Attributes -eq "Archive" -and $_.Extension -eq ".zip"} | Sort-Object -Property CreationTime -Descending:$false | Select-Object -First 1
Logging "INFO" "Remove Zip: $Zip"
$Zip.FullName | Remove-Item -Recurse -Force
}
#Check if Backupdirs and Destination is available
function Check-Dir {
Logging "INFO" "Check if BackupDir and Destination exists"
if (!(Test-Path $BackupDirs)) {
return $false
Logging "Error" "$BackupDirs does not exist"
}
if (!(Test-Path $Destination)) {
return $false
Logging "Error" "$Destination does not exist"
}
}
#Save all the Files
# note - if the folders are empty that are being copied you will see errors
# this shouldn't affect the backup --Tom
Function Make-Backup {
Logging "INFO" "Started the Backup"
$Files=#()
$SumMB=0
$SumItems=0
$SumCount=0
$colItems=0
Logging "INFO" "Count all files and create the Top Level Directories"
}
foreach ($Backup in $BackupDirs) {
$colItems = (Get-ChildItem $Backup -Recurse -File | Where-Object {$_.mode -notmatch "h"} | Measure-Object -property length -sum)
$Items=0
$FilesCount += Get-ChildItem $Backup -Recurse -File | Where-Object {$_.mode -notmatch "h"}
Copy-Item -Path $Backup -Destination $Backupdir -Force -ErrorAction SilentlyContinue
$SumMB+=$colItems.Sum.ToString()
$SumItems+=$colItems.Count
$TotalMB="{0:N2}" -f ($SumMB / 1MB) + " MB of Files"
Logging "INFO" "There are $SumItems Files with $TotalMB to copy"
foreach ($Backup in $BackupDirs) {
$Index=$Backup.LastIndexOf("\")
$SplitBackup=$Backup.substring(0,$Index)
$Files = Get-ChildItem $Backup -Recurse | select * | Where-Object {$_.mode -notmatch "h" -and $_.fullname -notmatch $exclude} | select fullname #$_.mode -notmatch "h" -and
foreach ($File in $Files) {
$restpath = $file.fullname.replace($SplitBackup,"")
try {
Copy-Item $file.fullname $($Backupdir+$restpath) -Force -ErrorAction SilentlyContinue |Out-Null
Logging "INFO" "$file was copied"
}
catch {
$ErrorCount++
Logging "ERROR" "$file returned an error an was not copied"
}
$Items += (Get-item $file.fullname).Length
$status = "Copy file {0} of {1} and copied {3} MB of {4} MB: {2}" -f $count,$SumItems,$file.Name,("{0:N2}" -f ($Items / 1MB)).ToString(),("{0:N2}" -f ($SumMB / 1MB)).ToString()
$Index=[array]::IndexOf($BackupDirs,$Backup)+1
$Text="Copy data Location {0} of {1}" -f $Index ,$BackupDirs.Count
Write-Progress -Activity $Text $status -PercentComplete ($Items / $SumMB*100)
if ($File.Attributes -ne "Directory") {$count++}
}
}
$SumCount+=$Count
$SumTotalMB="{0:N2}" -f ($Items / 1MB) + " MB of Files"
Logging "INFO" "----------------------"
Logging "INFO" "Copied $SumCount files with $SumTotalMB"
Logging "INFO" "$ErrorCount Files could not be copied"
# Send e-mail with reports as attachments
if ($SendEmail -eq $true) {
$EmailSubject = "Backup Email $(get-date -format MM.yyyy)"
$EmailBody = "Backup Script $(get-date -format MM.yyyy) (last Month).`nYours sincerely `Matthew - SYSTEM ADMINISTRATOR"
Logging "INFO" "Sending e-mail to $EmailTo from $EmailFrom (SMTPServer = $EmailSMTP) "
### the attachment is $log
Send-MailMessage -To $EmailTo -From $EmailFrom -Subject $EmailSubject -Body $EmailBody -SmtpServer $EmailSMTP -attachment $Log
}
}
#create Backup Dir
Create-Backupdir
Logging "INFO" "----------------------"
Logging "INFO" "Start the Script"
#Check if Backupdir needs to be cleaned and create Backupdir
$Count=(Get-ChildItem $Destination | where {$_.Attributes -eq "Directory"}).count
Logging "INFO" "Check if there are more than $Versions Directories in the Backupdir"
if ($count -gt $Versions)
{
Delete-Backupdir
}
$CountZip=(Get-ChildItem $Destination | where {$_.Attributes -eq "Archive" -and $_.Extension -eq ".zip"}).count
Logging "INFO" "Check if there are more than $Versions Zip in the Backupdir"
if ($CountZip -gt $Versions) {
Delete-Zip
}
#Check if all Dir are existing and do the Backup
$CheckDir=Check-Dir
if ($CheckDir -eq $false) {
Logging "ERROR" "One of the Directory are not available, Script has stopped"
} else {
Make-Backup
$Enddate=Get-Date #-format dd.MM.yyyy-HH:mm:ss
$span = $EndDate - $StartDate
$Minutes=$span.Minutes
$Seconds=$Span.Seconds
Logging "INFO" "Backupduration $Minutes Minutes and $Seconds Seconds"
Logging "INFO" "----------------------"
Logging "INFO" "----------------------"
if ($Zip)
{
Logging "INFO" "Compress the Backup Destination"
if ($Use7ZIP)
{
Logging "INFO" "Use 7ZIP"
if (-not (test-path "$env:ProgramFiles\7-Zip\7z.exe")) {Logging "WARNING" "7Zip not found"}
set-alias sz "$env:ProgramFiles\7-Zip\7z.exe"
#sz a -t7z "$directory\$zipfile" "$directory\$name"
if ($UseStaging -and $Zip)
{
$Zip=$Staging+("\"+$Backupdir.Replace($Staging,'').Replace('\','')+".zip")
sz a -t7z $Zip $Backupdir
Logging "INFO" "Move Zip to Destination"
Move-Item -Path $Zip -Destination $Destination
if ($ClearStaging)
{
Logging "INFO" "Clear Staging"
Get-ChildItem -Path $Staging -Recurse -Force | remove-item -Confirm:$false -Recurse
}
}
else
{
sz a -t7z ($Destination+("\"+$Backupdir.Replace($Destination,'').Replace('\','')+".zip")) $Backupdir
}
}
else
{
Logging "INFO" "Use Powershell Compress-Archive"
Compress-Archive -Path $Backupdir -DestinationPath ($Destination+("\"+$Backupdir.Replace($Destination,'').Replace('\','')+".zip")) -CompressionLevel Optimal -Force
}
If ($RemoveBackupDestination)
{
Logging "INFO" "Backupduration $Minutes Minutes and $Seconds Seconds"
#Remove-Item -Path $BackupDir -Force -Recurse
get-childitem -Path $BackupDir -recurse -Force | remove-item -Confirm:$false -Recurse
get-item -Path $BackupDir | remove-item -Confirm:$false -Recurse
}
}
}
Write-Host "Press any key to close ..."
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
The output of the log file shows the failure here:
03.05.2019-15:01:04 - ERROR: #{FullName=C:\Users\REDACTED\Desktop\Microsoft Edge.lnk} returned an error an was not copied
03.05.2019-15:01:04 - ERROR: #{FullName=C:\Users\REDACTED\Desktop\Microsoft Teams.lnk} returned an error an was not copied
03.05.2019-15:01:04 - ERROR: #{FullName=C:\Users\REDACTED\Desktop\PRMS Multi-Session.lnk} returned an error an was not copied
03.05.2019-15:01:04 - ERROR: #{FullName=C:\Users\REDACTED\Desktop\PRMS.lnk} returned an error an was not copied
Here is the output Powershell shows
03.05.2019-15:36:07 - ERROR: #{FullName=C:\Users\REDACTED\Desktop\desktop.ini} returned an error an was not copied
03.05.2019-15:36:07 - ERROR: #{FullName=C:\Users\REDACTED\Desktop\REDACTED Logos & Documents.lnk} returned an error an was not copied
03.05.2019-15:36:07 - ERROR: #{FullName=C:\Users\REDACTED\Desktop\REDACTED VPN - Shortcut.lnk} returned an error an was not copied
03.05.2019-15:36:07 - ERROR: #{FullName=C:\Users\REDACTED\Desktop\REDACTED_DigitalNET (J) - Shortcut.lnk} returned an error an was not copied
03.05.2019-15:36:07 - ERROR: #{FullName=C:\Users\REDACTED\Desktop\REDACTED_Sales (S) - Shortcut.lnk} returned an error an was not copied
03.05.2019-15:36:07 - ERROR: #{FullName=C:\Users\REDACTED\Desktop\Slack.lnk} returned an error an was not copied
03.05.2019-15:36:07 - ERROR: #{FullName=C:\Users\REDACTED\Desktop\REDACTED (U) - Shortcut.lnk} returned an error an was not copied
03.05.2019-15:36:07 - ERROR: #{FullName=C:\Users\REDACTED\Desktop\Visual Studio 2019.lnk} returned an error an was not copied
03.05.2019-15:36:07 - INFO: #{FullName=C:\Users\REDACTED\Documents\ConnectWiseControl} was copied
03.05.2019-15:36:07 - INFO: #{FullName=C:\Users\REDACTED\Documents\OneNote Notebooks} was copied
03.05.2019-15:36:07 - INFO: #{FullName=C:\Users\REDACTED\Documents\Visual Studio 2019} was copied
I currently have a powershell script, which print out some information regarding the files which passed in as argument..
The command for executing the script, it done as such:
.\myscript.ps1 -accessitem C:\folder
I want to apply the script on all files and folder on the drive C:, is it possible i for loop to list all files, and pass the path as argument for the script?
The script:
[CmdletBinding()]
Param (
[Parameter(Mandatory=$True,Position=0)]
[String]$AccessItem
)
$ErrorActionPreference = "SilentlyContinue"
If ($Error) {
$Error.Clear()
}
$RepPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
$RepPath = $RepPath.Trim()
$str = $AccessItem -replace ':',''
$str = $AccessItem -replace '/','.'
$FinalReport = "$RepPath\"+$str+".csv"
$ReportFile1 = "$RepPath\NTFSPermission_Report.txt"
If (!(Test-Path $AccessItem)) {
Write-Host
Write-Host "`t Item $AccessItem Not Found." -ForegroundColor "Yellow"
Write-Host
}
Else {
If (Test-Path $FinalReport) {
Remove-Item $FinalReport
}
If (Test-Path $ReportFile1) {
Remove-Item $ReportFile1
}
Write-Host
Write-Host "`t Working. Please wait ... " -ForegroundColor "Yellow"
Write-Host
## -- Create The Report File
$ObjFSO = New-Object -ComObject Scripting.FileSystemObject
$ObjFile = $ObjFSO.CreateTextFile($ReportFile1, $True)
$ObjFile.Write("NTFS Permission Set On -- $AccessItem `r`n")
$ObjFile.Close()
$ObjFile = $ObjFSO.CreateTextFile($FinalReport, $True)
$ObjFile.Close()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($ObjFSO) | Out-Null
Remove-Variable ObjFile
Remove-Variable ObjFSO
If((Get-Item $AccessItem).PSIsContainer -EQ $True) {
$Result = "ItemType -- Folder"
}
Else {
$Result = "ItemType -- File"
}
$DT = Get-Date -Format F
Add-Content $ReportFile1 -Value ("Report Created As On $DT")
Add-Content $ReportFile1 "=================================================================="
$Owner = (Get-Item -LiteralPath $AccessItem).GetAccessControl() | Select Owner
$Owner = $($Owner.Owner)
$Result = "$Result `t Owner -- $Owner"
Add-Content $ReportFile1 "$Result `n"
(Get-Item -LiteralPath $AccessItem).GetAccessControl() | Select * -Expand Access | Select IdentityReference, FileSystemRights, AccessControlType, IsInherited, InheritanceFlags, PropagationFlags | Export-CSV -Path "$RepPath\NTFSPermission_Report2.csv" -NoTypeInformation
Add-Content $FinalReport -Value (Get-Content $ReportFile1)
Add-Content $FinalReport -Value (Get-Content "$RepPath\NTFSPermission_Report2.csv")
Remove-Item $ReportFile1
Remove-Item "$RepPath\NTFSPermission_Report2.csv"
Invoke-Item $FinalReport
}
If ($Error) {
$Error.Clear()
}
I would prefer a outside command doing this, as workings of the script should not be altered, it it used for single file testing..
There are two ways to do this:
Add -Recurse Flag to the script
Run the script on each directory
I'm going with option two since the script looks complicated enough that I don't want to touch it.
$path_to_script = "C:\path\to\myscript.ps1"
$start_directory = "C:\folder"
# Call Script on Parent Directory
& "$path_to_script" -AccessItem "$start_directory"
# Call Script on any Child Directories within the "$start_directory"
foreach($child in (ls "$start_directory" -Recurse -Directory))
{
$path = $child.FullName
& "$path_to_script" -AccessItem "$path"
}
Basically, I'm calling the script on the parent directory and any sub-directories within the parent directory.
I'm trying to create a powershell script that deletes user profiles older than 30 days with powershell and excluding some users like admins accounts.
I've though maybe the script has to be signed by the domaincontroller or something, but i'm not sure if that will be the solution.
When i try to run it on a other directory it works but when i use it on c:\Users i get an error
Does anyone know what i have to change?
Error:
Set-ExecutionPolicy : Windows PowerShell updated your execution policy successfully, but the setting is overridden by a
policy defined at a more specific scope. Due to the override, your shell will retain its current effective execution
policy of RemoteSigned. Type "Get-ExecutionPolicy -List" to view your execution policy settings. For more information p
lease see "Get-Help Set-ExecutionPolicy".
At line:1 char:46
+ ... -ne 'AllSigned') { Set-ExecutionPolicy -Scope Process Bypass }; & 'H ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (:) [Set-ExecutionPolicy], SecurityException
+ FullyQualifiedErrorId : ExecutionPolicyOverride,Microsoft.PowerShell.Commands.SetExecutionPolicyCommand
The Code:
$Now = Get-Date
$Days = "15"
$TargetFolder = "C:\Users"
$LastWrite = $Now.AddDays(-$Days)
$Folders = get-childitem -path $TargetFolder |
Where {$_.psIsContainer -eq $true} |
Where {$_.LastWriteTime -le "$LastWrite"}
foreach ($Folder in $Folders)
{
if($Folder -notlike "user1")
{
if($Folder -notlike "Administrator")
{
if($Folder -notlike "user2")
{
if($Folder -notlike "Public")
{
if($Folder -notlike "NetworkService")
{
if($Folder -notlike "LocalService")
{
if($Folder -notlike "user3")
{
if($Folder -notlike "user4")
{
write-host "Deleting $Folder" -ForegroundColor Green
Remove-Item -recurse -Force C:\Users\$Folder
#Write-Host -NoNewLine "Press any key to continue... `n";
#$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown");
}
else
{
write-host "Cannot delete $Folder" -ForegroundColor Red
}
}
else
{
write-host "Cannot delete $Folder" -ForegroundColor Red
}
}
else
{
write-host "Cannot delete $Folder" -ForegroundColor Red
}
}
else
{
write-host "Cannot delete $Folder" -ForegroundColor Red
}
}
else
{
write-host "Cannot delete $Folder" -ForegroundColor Red
}
}
else
{
write-host "Cannot delete $Folder" -ForegroundColor Red
}
}
else
{
write-host "Cannot delete $Folder" -ForegroundColor Red
}
}
else
{
write-host "Cannot delete $Folder" -ForegroundColor Red
}
}
Here is my script which I have used sometime ago:
Most code is commented, so that should be clear. If you have any questions please ask.
# ********************************************************************************************************(**
# * **
# * Short description: Check profiles if they can be deleted - unused profiles will be deleted. **
# * Full description: * User running this script can specify the time which will check if profile contains **
# * any newer files than limit. If yes such profile is skipped. **
# * * User can specify which directories will be excepted from this search **
# * * User can specify which file suffixes will be ignored when the date will be checked **
# * * User can specify custom path for the profiles **
# * **
# * Creator : Patrik Svestka **
# * Created : 21/08/2017 **
# * Version : 1.0.1 **
# * **
# * Changes description: 1.0.0 - First Public version - Init release **
# * 1.0.1 - Added license type, minor changes to the header **
# * **
# * PowerShell compatibility: 2.0 , 4.0 and probably newer (untested) **
# * PowerShell tested versions: v2.0.50727, v4.0.30319 **
# * **
# * License: MIT **
# * **
# * TODO: ability to run the script remotely **
# to test remote connection - Get-WmiObject -ComputerName <server_name> Win32_Service -Credential $credentials
# Or manually from PowerShellEnter-PSSession <server_name> -Credential domain\<user_id>
# ***********************************************************************************************************
# **********************************************************
# Test run?
# **********************************************************
# when you want to test what will be deleted
$test_run = $true;
If ($test_run) {
Write-Warning -message 'Test run ENABLED - for actual DELETION set $test_run to $false' -verbose;
"`n";"`n";
}
# **********************************************************
# User configuration
# **********************************************************
# $credentials = 'domain\<user_id>';
# $server_name = '<server>';
# Profiles that contain file newer than 90 days will be exempted from deletion
$time_definition=#{'1m'="-0"};
# TODO: test for more periods - not tested yet!
# e.g more time frames - $time_definition=#{'1m'="-30"; '3m'="-90"; '6m'="-180"; '12m'="-360"; '18m'="-540"}
# running script path
$current_path = (Resolve-Path .\).Path;
$log_file = "$($current_path)\delete_files.log";
$folder_to_cleanse = 'E:\t\temp_profiles\'; #'C:\prg'
$excluded_directories = [System.Collections.ArrayList]#();
# All excluded profiles:
$excluded_directories.Add('All Users') | Out-null;
$excluded_directories.Add('Administrator') | Out-null;
$excluded_directories.Add('Default User') | Out-null;
$excluded_directories.Add('LocalService') | Out-null;
$excluded_directories.Add('NetworkService') | Out-null;
# Extensions excluded from date validation - these files will not influence the date check
# (will be deleted too if all others are found older)
$excluded_file_types = [System.Collections.ArrayList]#();
#$excluded_file_types.Add("*.bat", "*.cmd", "*.ps1") | Out-null;
$profile_directories = [System.Collections.ArrayList]#();
# **********************************************************
# The script's start
# **********************************************************
$newer_file_exist = $Null;
$files_to_delete = $Null;
# If previous log file exists delete it (only during test run)
If ((Test-Path -Path "$log_file") -and ($test_run)) {
Write-Verbose "Deleting previous log file $log_file." -verbose;
Remove-Item $log_file
}
# get all directories except excluded ones
$profile_directories = Get-ChildItem -Path $folder_to_cleanse -exclude $excluded_directories | Where-Object {$_.PSIsContainer -eq $True} | % { $_.Name }
# if $profile_directories found to be deleted => exit
If ([String]::IsNullOrEmpty($profile_directories)) {
Write-Warning -message "No profile directories to delete. Exiting." -verbose;
Exit;
}
# search in profile directories that are left after exclusion
# for all periods defined in time_definition
ForEach ($profile in $profile_directories) {
ForEach ($time in $time_definition.GetEnumerator()) {
Write-Verbose -message "Now processing the following profile: $folder_to_cleanse$profile." -verbose;
$test_current_pathPath = Test-Path -Path "$folder_to_cleanse$profile";
If ($test_current_pathPath) {
# check if any newer than $time_definition are present within the profile structure
# LastAccesstime can be empty! It is better, less issues, to use LastWriteTime. If you must use LastAccessTime use a check for ::IsNullOrEmpty
# LastWriteTime must be greater than current day - $time.Name (e.g. -90 days)
$newer_file_exist += Get-ChildItem -Path "$folder_to_cleanse$profile" -recurse -Force -exclude $excluded_file_types | Where-Object {$_.PSIsContainer -eq $FALSE} | where {($_.LastWriteTime).ToString('yyyy-MM-dd') -gt (get-date).adddays($time_definition.$($time.Name)).ToString('yyyy-MM-dd')};
}
# if any new file than the limit found the whole profile directory will be skipped (testing if $newer_file_exist $null)
If ($newer_file_exist) {
# add the top directory into excluded directory
$excluded_directories.Add($profile) | Out-null;
$newer_file_exist=$Null;
Write-Verbose -message "The profile $profile will be excluded from deletion process." -verbose;
continue;
}
}
}
# excluding the directories with newer files than limit defined by user
$profiles_with_path = Get-ChildItem -Path $folder_to_cleanse -exclude $excluded_directories | Where-Object {$_.PSIsContainer -eq $True}
# perhaps all $directories are now excluded?
If ([String]::IsNullOrEmpty($profiles_with_path)) {
Write-Warning -message "No directories to delete all probably filtered. Exiting." -verbose;
Exit;
}
# get all files to be deleted
ForEach ($dir in $profiles_with_path) {
# to check
$test_current_pathPath = Test-Path -Path $dir
If ($test_current_pathPath) {
#write-host 'Currently writing for these months:'$($time.Name);
$files_to_delete += Get-ChildItem -Path $dir -recurse -Force | Where-Object {$_.PSIsContainer -eq $FALSE} | % { $_.FullName }
}
}
# **********************************************************
# Messages for the user
# **********************************************************
Write-Verbose -message "List of profiles to be deleted:" -verbose;
ForEach ($profile_to_delete in $profiles_with_path) {
Write-Verbose -message "$profile_to_delete`n" -verbose;
}
Write-Verbose -message "The total count of non-excluded profile directories: $($profiles_with_path.Count)" -verbose;
Write-Verbose -message "==========================`n`n" -verbose;
Write-Verbose -message "List of excluded directories:`n" -verbose;
ForEach ($excluded_profile in $excluded_directories) {
Write-Verbose -message "$folder_to_cleanse$excluded_profile`n" -verbose;
}
Write-Verbose -message "Total count of excluded directories: $($excluded_directories.Count)" -verbose;
Write-Verbose -message "==========================`n`n" -verbose;
Write-Verbose -message "Total directory count (both to be deleted and excluded): $($($profiles_with_path.Count)+ $($excluded_directories.Count))`n" -verbose;
# **********************************************************
# Test run or actual deletion process
# **********************************************************
If ($test_run) {
ForEach ($file in $files_to_delete) {
$file | Out-file -Encoding 'Unicode' -FilePath $log_file -Append # >> $log_file
}
Write-Verbose 'This number of files would be deleted:' -verbose;
Write-Verbose "Found $($files_to_delete.Count) files marked for deletion." -verbose;
} Else {
$files_deleted = 0;
# delete files
If ($files_to_delete) {
ForEach ($file in $files_to_delete) {
#Remove-Item $file -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item $file -Force -ErrorAction SilentlyContinue
If ($? -eq $true) {
$files_deleted ++;
#Write-Verbose -Verbose "$File deleted successfully!"
}
}
}
# delete directories
$directories_deleted = 0;
ForEach ($dir in $profiles_with_path) { #
Remove-Item $dir -Recurse -Force -ErrorAction SilentlyContinue
If ($? -eq $true) {
$directories_deleted ++;
#Write-Verbose -Verbose "$File deleted successfully!"
}
}
Return "Total files to be deleted: $($files_to_delete.count)","Total files Deleted: $files_deleted", "Total Directories deleted: $directories_deleted"
}
I have the below code and currently it loads all the information on screen. I want it to log to a log file on D:\Apps\Logs.
The log file needs to have the name of the computer it is loading against - so COMPUTERNAME.log
Any idea how I can do this?
Thanks
$computer = gc env:computername
$onetcp = ((get-childitem c:\windows\system32\drivers\tcpip.sys).Versioninfo.ProductMajorPart).tostring() $twotcp = ((get-childitem c:\windows\system32\drivers\tcpip.sys).Versioninfo.ProductMinorPart).tostring() $threetcp = ((get-childitem c:\windows\system32\drivers\tcpip.sys).Versioninfo.ProductBuildPart).tostring() $fourtcp = ((get-childitem c:\windows\system32\drivers\tcpip.sys).Versioninfo.ProductPrivatePart).tostring()
$onedfsr = ((get-childitem c:\windows\system32\dfsrs.exe).Versioninfo.ProductMajorPart).tostring() $twodfsr = ((get-childitem c:\windows\system32\dfsrs.exe).Versioninfo.ProductMinorPart).tostring() $threedfsr = ((get-childitem c:\windows\system32\dfsrs.exe).Versioninfo.ProductBuildPart).tostring() $fourdfsr = ((get-childitem c:\windows\system32\dfsrs.exe).Versioninfo.ProductPrivatePart).tostring()
write-host TCPIP.sys Version on $computer is: "$onetcp.$twotcp.$threetcp.$fourtcp" Write-Host write-host DFSRS.exe Version on $computer is: "$onedfsr.$twodfsr.$threedfsr.$fourdfsr"
Write-Host
If (get-wmiobject win32_share | where-object {$_.Name -eq "REMINST"}) { Write-Host "The REMINST share exists on $computer" } Else { Write-Host "The REMINST share DOES NOT exist on $computer - Please create as per standards" } Write-Host
$hotfix1 = Get-HotFix -Id KB2450944 -ErrorAction SilentlyContinue $hotfix2 = Get-HotFix -Id KB2582284 -ErrorAction SilentlyContinue $hotfix3 = Get-HotFix -Id KB979808 -ErrorAction SilentlyContinue
If ($hotfix1) { Write-Host "Hotfix KB2450944 is installed"
-BackgroundColor Green -ForegroundColor Black } else { Write-Host "Hotfix KB2450944 is NOT installed - Please ensure you install this hotfix" -ForegroundColor "red" }
If ($hotfix2) { Write-Host "Hotfix KB2582284 is installed"
-BackgroundColor Green -ForegroundColor Black } else { Write-Host "Hotfix KB2582284 is NOT installed - Please ensure you install this hotfix" -ForegroundColor "red" }
If ($hotfix3) { Write-Host "Hotfix KB979808 is installed"
-BackgroundColor Green -ForegroundColor Black } else { Write-Host "Hotfix KB979808 is NOT installed - Please ensure you install this hotfix" -ForegroundColor "red" }
Put this at the top of your file:
$Logfile = "D:\Apps\Logs\$(gc env:computername).log"
Function LogWrite
{
Param ([string]$logstring)
Add-content $Logfile -value $logstring
}
Then replace your Write-host calls with LogWrite.
A function that takes these principles a little further.
Add's timestamps - can't have a log without timestamps.
Add's a level (uses INFO by default) meaning you can highlight big issues.
Allows for optional console output. If you don't set a log destination, it simply pumps it out.
Function Write-Log {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$False)]
[ValidateSet("INFO","WARN","ERROR","FATAL","DEBUG")]
[String]
$Level = "INFO",
[Parameter(Mandatory=$True)]
[string]
$Message,
[Parameter(Mandatory=$False)]
[string]
$logfile
)
$Stamp = (Get-Date).toString("yyyy/MM/dd HH:mm:ss")
$Line = "$Stamp $Level $Message"
If($logfile) {
Add-Content $logfile -Value $Line
}
Else {
Write-Output $Line
}
}
I believe this is the simplest way of putting all what it is on the screen into a file. It is a native PS CmdLet so you don't have to change or install anything in your script
Start-Transcript -Path Computer.log
Write-Host "everything will end up in Computer.log"
Stop-Transcript
You can also add -Append to append instead the content [Thanks #scipilot for the tip!]
function WriteLog
{
Param ([string]$LogString)
$LogFile = "C:\$(gc env:computername).log"
$DateTime = "[{0:MM/dd/yy} {0:HH:mm:ss}]" -f (Get-Date)
$LogMessage = "$Datetime $LogString"
Add-content $LogFile -value $LogMessage
}
WriteLog "This is my log message"
Using this Log-Entry framework:
Script:
Function Main {
Log -File "D:\Apps\Logs\$Env:computername.log"
$tcp = (get-childitem c:\windows\system32\drivers\tcpip.sys).Versioninfo.ProductVersionRaw
$dfs = (get-childitem C:\Windows\Microsoft.NET\Framework\v2.0.50727\dfsvc.exe).Versioninfo.ProductVersionRaw
Log "TCPIP.sys Version on $computer is:" $tcp
Log "DFSVC.exe Version on $computer is:" $dfs
If (get-wmiobject win32_share | where-object {$_.Name -eq "REMINST"}) {Log "The REMINST share exists on $computer"}
Else {Log "The REMINST share DOES NOT exist on $computer - Please create as per standards"}
"KB2450944", "KB3150513", "KB3176935" | ForEach {
$hotfix = Get-HotFix -Id $_ -ErrorAction SilentlyContinue
If ($hotfix) {Log -Color Green Hotfix $_ is installed}
Else {Log -Color Red Hotfix $_ " is NOT installed - Please ensure you install this hotfix"}
}
}
Screen output:
Log File (at D:\Apps\Logs\<computername>.log):
2017-05-31 Write-Log (version: 01.00.02, PowerShell version: 5.1.14393.1198)
19:19:29.00 C:\Users\User\PowerShell\Write-Log\Check.ps1
19:19:29.47 TCPIP.sys Version on is: {Major: 10, Minor: 0, Build: 14393, Revision: 1066, MajorRevision: 0, MinorRevision: 1066}
19:19:29.50 DFSVC.exe Version on is: {Major: 2, Minor: 0, Build: 50727, Revision: 8745, MajorRevision: 0, MinorRevision: 8745}
19:19:29.60 The REMINST share DOES NOT exist on - Please create as per standards
Error at 25,13: Cannot find the requested hotfix on the 'localhost' computer. Verify the input and run the command again.
19:19:33.41 Hotfix KB2450944 is NOT installed - Please ensure you install this hotfix
19:19:37.03 Hotfix KB3150513 is installed
19:19:40.77 Hotfix KB3176935 is installed
19:19:40.77 End
Gist with log rotation: https://gist.github.com/barsv/85c93b599a763206f47aec150fb41ca0
Usage:
. .\logger.ps1
Write-Log "debug message"
Write-Log "info message" "INFO"
You might just want to use the new TUN.Logging PowerShell module, this can also send a log mail. Just use the Start-Log and/or Start-MailLog cmdlets to start logging and then just use Write-HostLog, Write-WarningLog, Write-VerboseLog, Write-ErrorLog etc. to write to console and log file/mail. Then call Send-Log and/or Stop-Log at the end and voila, you got your logging.
Just install it from the PowerShell Gallery via
Install-Module -Name TUN.Logging
Or just follow the link: https://www.powershellgallery.com/packages/TUN.Logging
Documentation of the module can be found here: https://github.com/echalone/TUN/blob/master/PowerShell/Modules/TUN.Logging/TUN.Logging.md
I've been playing with this code for a while now and I have something that works well for me. Log files are numbered with leading '0' but retain their file extension. And I know everyone likes to make functions for everything but I started to remove functions that performed 1 simple task. Why use many word when few do trick? Will likely remove other functions and perhaps create functions out of other blocks. I keep the logger script in a central share and make a local copy if it has changed, or load it from the central location if needed.
First I import the logger:
#Change directory to the script root
cd $PSScriptRoot
#Make a local copy if changed then Import logger
if(test-path "D:\Scripts\logger.ps1"){
if (Test-Path "\\<server>\share\DCS\Scripts\logger.ps1") {
if((Get-FileHash "\\<server>\share\DCS\Scripts\logger.ps1").Hash -ne (Get-FileHash "D:\Scripts\logger.ps1").Hash){
rename-Item -path "..\logger.ps1" -newname "logger$(Get-Date -format 'yyyyMMdd-HH.mm.ss').ps1" -force
Copy-Item "\\<server>\share\DCS\Scripts\logger.ps1" -destination "..\" -Force
}
}
}else{
Copy-Item "\\<server>\share\DCS\Scripts\logger.ps1" -destination "..\" -Force
}
. "..\logger.ps1"
Define the log file:
$logfile = (get-location).path + "\Log\" + $QProfile.replace(" ","_") + "-$metricEnv-$ScriptName.log"
What I log depends on debug levels that I created:
if ($Debug -ge 1){
$message = "<$pid>Debug:$Debug`-Adding tag `"MetricClass:temp`" to $host_name`:$metric_name"
Write-Log $message $logfile "DEBUG"
}
I would probably consider myself a bit of a "hack" when it comes to coding so this might not be the prettiest but here is my version of logger.ps1:
# all logging settins are here on top
param(
[Parameter(Mandatory=$false)]
[string]$logFile = "$(gc env:computername).log",
[Parameter(Mandatory=$false)]
[string]$logLevel = "DEBUG", # ("DEBUG","INFO","WARN","ERROR","FATAL")
[Parameter(Mandatory=$false)]
[int64]$logSize = 10mb,
[Parameter(Mandatory=$false)]
[int]$logCount = 25
)
# end of settings
function Write-Log-Line ($line, $logFile) {
$logFile | %{
If (Test-Path -Path $_) { Get-Item $_ }
Else { New-Item -Path $_ -Force }
} | Add-Content -Value $Line -erroraction SilentlyCOntinue
}
function Roll-logFile
{
#function checks to see if file in question is larger than the paramater specified if it is it will roll a log and delete the oldes log if there are more than x logs.
param(
[string]$fileName = (Get-Date).toString("yyyy/MM/dd HH:mm:ss")+".log",
[int64]$maxSize = $logSize,
[int]$maxCount = $logCount
)
$logRollStatus = $true
if(test-path $filename) {
$file = Get-ChildItem $filename
# Start the log-roll if the file is big enough
#Write-Log-Line "$Stamp INFO Log file size is $($file.length), max size $maxSize" $logFile
#Write-Host "$Stamp INFO Log file size is $('{0:N0}' -f $file.length), max size $('{0:N0}' -f $maxSize)"
if($file.length -ge $maxSize) {
Write-Log-Line "$Stamp INFO Log file size $('{0:N0}' -f $file.length) is larger than max size $('{0:N0}' -f $maxSize). Rolling log file!" $logFile
#Write-Host "$Stamp INFO Log file size $('{0:N0}' -f $file.length) is larger than max size $('{0:N0}' -f $maxSize). Rolling log file!"
$fileDir = $file.Directory
$fbase = $file.BaseName
$fext = $file.Extension
$fn = $file.name #this gets the name of the file we started with
function refresh-log-files {
Get-ChildItem $filedir | ?{ $_.Extension -match "$fext" -and $_.name -like "$fbase*"} | Sort-Object lastwritetime
}
function fileByIndex($index) {
$fileByIndex = $files | ?{($_.Name).split("-")[-1].trim("$fext") -eq $($index | % tostring 00)}
#Write-Log-Line "LOGGER: fileByIndex = $fileByIndex" $logFile
$fileByIndex
}
function getNumberOfFile($theFile) {
$NumberOfFile = $theFile.Name.split("-")[-1].trim("$fext")
if ($NumberOfFile -match '[a-z]'){
$NumberOfFile = "01"
}
#Write-Log-Line "LOGGER: GetNumberOfFile = $NumberOfFile" $logFile
$NumberOfFile
}
refresh-log-files | %{
[int32]$num = getNumberOfFile $_
Write-Log-Line "LOGGER: checking log file number $num" $logFile
if ([int32]$($num | % tostring 00) -ge $maxCount) {
write-host "Deleting files above log max count $maxCount : $_"
Write-Log-Line "LOGGER: Deleting files above log max count $maxCount : $_" $logFile
Remove-Item $_.fullName
}
}
$files = #(refresh-log-files)
# Now there should be at most $maxCount files, and the highest number is one less than count, unless there are badly named files, eg non-numbers
for ($i = $files.count; $i -gt 0; $i--) {
$newfilename = "$fbase-$($i | % tostring 00)$fext"
#$newfilename = getFileNameByNumber ($i | % tostring 00)
if($i -gt 1) {
$fileToMove = fileByIndex($i-1)
} else {
$fileToMove = $file
}
if (Test-Path $fileToMove.PSPath) { # If there are holes in sequence, file by index might not exist. The 'hole' will shift to next number, as files below hole are moved to fill it
write-host "moving '$fileToMove' => '$newfilename'"
#Write-Log-Line "LOGGER: moving $fileToMove => $newfilename" $logFile
# $fileToMove is a System.IO.FileInfo, but $newfilename is a string. Move-Item takes a string, so we need full path
Move-Item ($fileToMove.FullName) -Destination $fileDir\$newfilename -Force
}
}
} else {
$logRollStatus = $false
}
} else {
$logrollStatus = $false
}
$LogRollStatus
}
Function Write-Log {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True)]
[string]
$Message,
[Parameter(Mandatory=$False)]
[String]
$logFile = "log-$(gc env:computername).log",
[Parameter(Mandatory=$False)]
[String]
$Level = "INFO"
)
#Write-Host $logFile
$levels = ("DEBUG","INFO","WARN","ERROR","FATAL")
$logLevelPos = [array]::IndexOf($levels, $logLevel)
$levelPos = [array]::IndexOf($levels, $Level)
$Stamp = (Get-Date).toString("yyyy/MM/dd HH:mm:ss:fff")
# First roll the log if needed to null to avoid output
$Null = #(
Roll-logFile -fileName $logFile -filesize $logSize -logcount $logCount
)
if ($logLevelPos -lt 0){
Write-Log-Line "$Stamp ERROR Wrong logLevel configuration [$logLevel]" $logFile
}
if ($levelPos -lt 0){
Write-Log-Line "$Stamp ERROR Wrong log level parameter [$Level]" $logFile
}
# if level parameter is wrong or configuration is wrong I still want to see the
# message in log
if ($levelPos -lt $logLevelPos -and $levelPos -ge 0 -and $logLevelPos -ge 0){
return
}
$Line = "$Stamp $Level $Message"
Write-Log-Line $Line $logFile
}
I'm rather new to PowerShell.
I found a few guides and mashed a little script together, though i do not seem to be able to set a prompt to ask for the source/destination.
The script is as following:
gci -path | Get-Random -Count 4 | mi -Destination C:\Temp
while(1) { sleep -sec 20; .\Power.ps1 }
For any response,
thanks in advance!
Use Read-Host:
Get-ChildItem -Path (Read-Host -Prompt 'Get path')
Here's an example, FWIW:
$source,$target = $null,$null
while ($source -eq $null){
$source = read-host "Enter source file name"
if (-not(test-path $source)){
Write-host "Invalid file path, re-enter."
$source = $null
}
elseif ((get-item $source).psiscontainer){
Write-host "Source must be a file, re-enter."
$source = $null
}
}
while ($target -eq $null){
$target = read-host "Enter source directory name"
if (-not(test-path $target)){
Write-host "Invalid directory path, re-enter."
$target = $null
}
elseif (-not (get-item $target).psiscontainer){
Write-host "Target must be a directory, re-enter."
$target = $null
}
}