Key not valid for use in specified state - A positional parameter cannot be found that accepts argument '+' - positional-parameter

I am trying to understand what this error actually means. I am new to PowerShell and cannot
figure this one out. I have searched for similar questions but the content differs to my
requirement.
In a nut shell the script is queering a data historian system for a batch/lot number and the
start time of that batch.
This script will run every minute using task scheduler. This has not been set up yet as I am
still in the testing phase.
I have set up a service account is order for the script to run. The details of which are
stored in a cred file.
The script creates a folder using this batch/lot number.
The script creates a log file with the batch number and the start date and time of the batch.
Then the script searches a source folder on the server when a file is uploaded from the
factory floor into the source folder the script moves the file into the already created folder
with the correct batch number.
If files that are outside of the batch start and end time then the files are moved to no batch
folder where they will be reviewed manually.
I have done tests whereby I manually added files to the source folder on the server and
everything worked and did not get the "a positional parameter cannot be found that accepts
argument "+" from the script.
I am looking into the server configuration and permission levels but to my knowledge, nothing
has changed. I cannot see what is wrong with the script but hopefully, someone can give me
some pointers.
Error Code below
`PS C:\Users\a-graydx2> E:\Kistler Script\Batch ID with log 2021-11-29.ps1
An error occurred:
Key not valid for use in specified state.
Add-Content : A positional parameter cannot be found that accepts argument '+'.
At E:\Kistler Script\Batch ID with log 2021-11-29.ps1:186 char:11
+ Add-Content -Path $ErrorFileName -Value (Get-Date -Format " ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Add-Content], ParameterBindingException
+ FullyQualifiedErrorId :
PositionalParameterNotFound,Microsoft.PowerShell.Commands.AddContentCommand
An error occurred:
Key not valid for use in specified state.
Add-Content : A positional parameter cannot be found that accepts argument '+'.
At E:\Kistler Script\Batch ID with log 2021-11-29.ps1:186 char:11
+ Add-Content -Path $ErrorFileName -Value (Get-Date -Format " ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Add-Content], ParameterBindingException
+ FullyQualifiedErrorId :
PositionalParameterNotFound,Microsoft.PowerShell.Commands.AddContentCommand`
Script is below
Thanks for your help
`# Declare global variables
$fmSourcePath = "E:\Kistler\CoMo Services\Data\5336_L1.4345277\"
$shSourcePath = "E:\Kistler\CoMo Services\Data\5338_L1.5338_L1\"
$fmDesinationPath = "E:\Kistler XML Files\FM\"
$shDesinationPath = "E:\Kistler XML Files\SH\"
$fmWebAPI = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
$shWebAPI = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# the path to stored credential
$credPath = "E:\Kistler Script\Cred.xml"
$logFileName = "BatchLog.txt"
#Path to the error log file
$ErrorFileName = "E:\Kistler Script\errorlog.txt"
function Move_Kistler_Files {
param (
[string]$url,
[string]$SourcePath,
[string]$DestinationPath
)
try {
# check for stored credential
if ( Test-Path $credPath ) {
#crendetial is stored, load it
$cred = Import-CliXml -Path $credPath
} else {
# no stored credential then: create store, get credential and save it
$parent = split-path $credpath -parent
if ( -not ( test-Path $parent ) ) {
New-Item -ItemType Directory -Force -Path $parent
}
$cred = get-credential
$cred | Export-CliXml -Path $credPath
}
# Get the current batch id using the Web-API call
$result = Invoke-RestMethod -Uri $url -Credential $Cred
$BatchID = $result.Value
$BatchFolder = $DestinationPath + $BatchID
Write-Host $BatchFolder
# Create a new folder in the destination path based on the Batch ID
If(!(test-path $BatchFolder))
{
New-Item -ItemType Directory -Force -Path $BatchFolder | Out-Null
# Add the current date/time to the log file
$LogFile = $DestinationPath + $logFileName
# if file exist Update the last record with the batch end date
If((test-path $LogFile)){
$txt = get-content $LogFile
$txt[$txt.length - 1 ] = $txt[$txt.length - 1 ] + ", " + (Get-Date)
$txt | set-content $LogFile
}else{
#add a header row in the file
Add-Content -Path $LogFile -Value "BatchID, StartDate, EndDate"
}
# create a new record in the log file with current Batch Id and date as start of
batch indicator
$msg = $BatchID + ", " + (Get-Date)
Add-Content -Path $LogFile -Value $msg
}
##############################################################################
# Copy the Kistler XML files from the source to the destination
##############################################################################
# get al the Kistler XML files in the source folder
$Files = get-childitem -path $SourcePath -Recurse | Where-Object {$_.Extension -eq ".XML"}
| Sort-Object LastWriteTime -Descending
# If we have files to process do it
if ($Files.Length -gt 0) {
# read back the batch start and end dates from the log table
$LogFile = $DestinationPath + $logFileName
$txt = get-content $LogFile
# Get the latest Batch Id and it's start date
$FileMoveCount = 0
$FileNotMoveCount = 0
$ptr = 1
$batchArray =$txt[$txt.length - $ptr ].Split(",")
$MoveToPath = $DestinationPath + $batchArray[0]
$batchStartDate = $batchArray[1]
#Process each XML file
Foreach ($File in $Files ) {
$FileTime = $File.LastWriteTime
#write-host $File.FullName $File.Name $FileTime $MoveToPath $batchStartDate
#if the XML file's date-time is older than the batch start time, skip to the
previus Batch Id and start time
while ( ([DateTime]$FileTime -lt [DateTime]$batchStartDate) -and ($ptr -lt
($txt.length)-1) ) {
#Write a log for the number of files copied
if ($FileMoveCount -gt 0){
Add-Content -Path $ErrorFileName -Value ((Get-Date -Format "dd/MM/yyyy
HH:mm") + ": " + $FileMoveCount + " XML files moved to " + $MoveToPath)
$FileMoveCount = 0
}
$ptr++
$batchArray =$txt[$txt.length - $ptr ].Split(",")
$MoveToPath = $DestinationPath + $batchArray[0]
$batchStartDate = $batchArray[1]
#write-host $MoveToPath $batchStartDate
}
#Copy the XML file to the destination folder
if ([DateTime]$FileTime -ge [DateTime]$batchStartDate){
Move-Item $File.FullName -Destination ($MoveToPath + "\" + $File.Name)
$FileMoveCount++
}else{
Move-Item $File.FullName -Destination ($DestinationPath + "\NoBatch\" +
$File.Name)
$FileNotMoveCount++
}
}
#Write a log for the number of files copied
if ($FileMoveCount -gt 0){
Add-Content -Path $ErrorFileName -Value ((Get-Date -Format "dd/MM/yyyy HH:mm") + ": "
+ $FileMoveCount + " XML files moved to " + $MoveToPath)
}
if ($FileNotMoveCount -gt 0){
Add-Content -Path $ErrorFileName -Value ((Get-Date -Format "dd/MM/yyyy HH:mm") + ":
Could not find batch ID for " + $FileNotMoveCount + " XML files " )
}
}
}catch{
#Write the error
Write-Host "An error occurred:" -ForegroundColor red
Write-Host $_ -ForegroundColor red
Add-Content -Path $ErrorFileName -Value (Get-Date -Format "dd/MM/yyyy HH:mm") + ": " +
$_
}
}
### Process the FM Kistler files
Move_Kistler_Files $fmWebAPI $fmSourcePath $fmDesinationPath
### Process the SH Kistler files
Move_Kistler_Files $shWebAPI $shSourcePath $shDesinationPath`

Related

How to delete an image file based on its dimensions with PowerShell

I am trying to delete an image based on the dimensions of said image, but I've run into a problem.
I am trying to delete images whose length or width are less than 490 pixels. However, the code I have tried throws an error for every item. This is the error:
Remove-Item : Cannot remove item (file path): The process cannot access the file
'(file path)' because it is being used by another process.
At line:6 char:9
+ Remove-Item $_
+ ~~~~~~~~~~~~~~
+ CategoryInfo : WriteError: ((file path):FileInfo) [Remove-Item], IOException
+ FullyQualifiedErrorId : RemoveFileSystemItemIOError,Microsoft.PowerShell.Commands.RemoveItemCommand
Here is my code:
[Void][Reflection.Assembly]::LoadWithPartialName("System.Drawing")
$(Get-ChildItem -Filter *.jpg).FullName | ForEach-Object {
$img = [Drawing.Image]::FromFile($_);
If (($img.Width -lt 490) -or ($img.Height -lt 490)) {
Remove-Item $_
}
}
I am not running any apparent processes that would be using the images. When using Handle64, it says that powershell.exe is using the files. Any help would be appreciated!
The $img object is keeping the file in use, so you need to dispose of that before you can delete the file:
Add-Type -AssemblyName System.Drawing
(Get-ChildItem -Filter '*.jpg' -File).FullName | ForEach-Object {
$img = [System.Drawing.Image]::FromFile($_)
$w = $img.Width
$h = $img.Height
# get rid of the Image object and release the lock on the file
$img.Dispose()
If (($w -lt 490) -or ($h -lt 490)) {
Remove-Item -Path $_
}
}

Powershell issues with parameters: 'Cannot convert value "" to type "System.Boolean"'

I wrote a PowerShell utility that takes in a couple parameters, and transfers files from a source directory to a destination directory.
Initially, all was done as a single function, and worked well enough.
Before adding some features, I broke repeated logic into its own function.
Then, the ISSUES began.
It appears that the Param() variables are seeded with incorrect values. Running the script yields the following:
PS ...> .\photoTransfer.ps1 E:\DCIM\100OLYMP
Cannot convert value "" to type "System.Boolean". Boolean parameters accept only Boolean values and numbers, such as
$True, $False, 1 or 0.
At C:\Users\SWPhantom\Desktop\admin\photoTransfer.ps1:85 char:3
+ [Parameter(Mandatory=$true, Position = 0, HelpMessage = "The path o ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : MetadataError: (:) [], ArgumentTransformationMetadataException
+ FullyQualifiedErrorId : RuntimeException
I can confirm that something's strange with
Write-Output "src: $source", which spits out src: True. (Expected to be src: E:\DCIM\100OLYMP)
HOWEVER: I can get the value I expect to be passed in with an $args[0].
I expect that the issue is simple, but I can't pick up on it, as this was my first foray into more... mature PowerShell scripting.
I am getting around the immediate problem by using the $args[i] method, but it'd be nice to not get an error message and use the seemingly Nice and orderly Params. (Especially since they seemed to work before I made the separate Transfer function).
Thanks!
Full code:
# Purpose: Transfer all photos from a memory card, to a destination folder, organized by year, month, date.
# Ensure that the Date Modified and Date Created is preserved.
function Transfer {
Param(
[Parameter(Mandatory, Position = 0)]
[string]$src,
[Parameter(Mandatory, Position = 1)]
[string]$dst,
[Parameter(Mandatory, Position = 2)]
[string]$extension
)
# Look at the source directory. Enumerate files to be sent over. (Only copy .ORF/.MOV files)
$files = Get-ChildItem -Path $src -Include $extension -Recurse
$numberOfFiles = $files.Count
if($numberOfFiles -eq 0) {
return "No $extension files found in $src!"
}
# Give user last chance to stop program. Show them number of files and destination folder.
Write-Output "Ensure the action is correct:"
read-host "Copying $numberOfFiles files from $src to $dst ?`nPress Enter to continue"
# Iteration for progress tracking.
$iter = 1
# Foreach file, check the Date Modified field. Make sure the destination folder has the folder structure like:
# Drive/Photos/YYYY/MM/DD/
# Where the YMD matches the Date Modified field of every photo.
foreach ($file in $files) {
$originalCreationTime = $file.LastWriteTime
[string]$year = $file.LastWriteTime.Year
[string]$month = $file.LastWriteTime.Month
[string]$date = $file.LastWriteTime.Day
# Add leading zero, if necessary
if($month.length -lt 2) {
$month = "0" + $month
}
if($date.length -lt 2) {
$date = "0" + $date
}
# Test the path of destinationPath/YYYY/MM/DD/
$path = $dst + "$year\$month\$date\"
if (!(Test-Path -Path $path)) {
if($verb) {
Write-Output " $path"
}
New-Item -ItemType Directory -Path $path
}
# The filepath exists!
if($verb) {
Write-Output " ($iter/$numberOfFiles) $file -> $path"
}
$iter += 1
Copy-Item $file.FullName -Destination $path
# Fix the Creation Time
$(Get-Item -Path "$path$($file.Name)").CreationTime=$originalCreationTime
}
Write-Output "`nCopying done!`n"
# Delete items?
Write-Output "Delete $numberOfItems items?"
$del = read-host "Deleting copied files from $src ?`nY to continue"
if($del -eq "Y") {
foreach ($file in $files) {
Remove-Item $file.FullName
}
}
}
Param(
# Source Folder
[Parameter(Mandatory=$true, Position = 0, HelpMessage = "The path of the source of the media")]
[Alias("s")]
[string]$source,
# Photo Destination
[Parameter(Mandatory=$false, Position = 1, HelpMessage = "The path of the folder you want to move photos to")]
[Alias("pd")]
[string]$photoDestination,
# Video Destination
[Parameter(Mandatory=$false, Position = 2, HelpMessage = "The path of the folder you want to move videos to")]
[Alias("vd")]
[string]$videoDestionation,
# Verbosity
[Parameter(Position = 3, HelpMessage = "Turn extra logging on or off")]
[Alias("v")]
[bool]$verb = $true
)
$usageHelpText = "usage:`n photoTransfer.ps1 <DriveName> <pathToDestinationRootFolder>`nex:`n .\photoTransfer.ps1 C T:\Photos"
#TODO: Solve this conundrum, where passing a via CMD
# Write-Output "Source before treatment: $($args[0])"
# Write-Output "Source before treatment: $($args[1])"
# Write-Output "Source before treatment: $($args[2])"
# Write-Output "Source before treatment: $($args[3])"
$source = $args[0]
$verb = $true
# I expect a drive name. If a ':' is missing, I add it.
if(!$source.Contains(":")) {
$source = $source + ":"
}
# The assumption is that the photos are coming from my Olympus camera, which has the following path to the files.
# $olympusFolderPath = "DCIM\100OLYMP\"
# $source += $olympusFolderPath
# Make sure the destination path has a terminating '\'
# if(!($photoDestination -match "\\$")) {
# $photoDestination = $photoDestination + "\"
# }
$photoDestination = "T:\Photos\"
$videoDestionation = "T:\Footage\"
# Check if the source and destination paths are valid.
if (!(Test-Path -Path $source)) {
Write-Output "Source disk ($source) doesn't exist`n$usageHelpText"
exit 0
}
if (!(Test-Path -Path $photoDestination)) {
Write-Output "Destination path ($photoDestination) doesn't exist`n$usageHelpText"
exit 0
}
if (!(Test-Path -Path $videoDestionation)) {
Write-Output "Destination path ($videoDestionation) doesn't exist`n$usageHelpText"
exit 0
}
Transfer $source $photoDestination "*.ORF"
Transfer $source $videoDestionation "*.MOV"

robocopy adds hidden symbol when creating folders

What I do, is copying photo files from SD card to HDD using powershell ps1 file and Windows PowerShell ISE.
I get a taken date from image exif and add it to destination path.
The problem is that robocopy creates folders and adds strange prefix, which I do not want to have.
As a result I can see two subfolders with same name "2020", one folder created by hand and the other created by robocopy.
This prefix is only seen when I list folders with CMD.
The prefix not seen in output.log and in powershell.
$copy_from = "G:\DCIM\100MSDCF\"
$copy_to = "C:\Photos\"
function GetDateTaken {
param (
[Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[Alias('FullName')]
[String]
$Path
)
begin {
$shell = New-Object -COMObject Shell.Application
}
process {
$returnvalue = 1 | Select-Object -Property Name, DateTaken, Folder
$returnvalue.Name = Split-Path $path -Leaf
$returnvalue.Folder = Split-Path $path
$shellfolder = $shell.Namespace($returnvalue.Folder)
$shellfile = $shellfolder.ParseName($returnvalue.Name)
$returnvalue.DateTaken = $shellfolder.GetDetailsOf($shellfile, 12)
$returnvalue.DateTaken
}
}
$file = Get-ChildItem -Path $copy_from -recurse -include ('*.jpg','*.arw')
$i = 0
$jpg = 0
$arw = 0
$logifile = 'output.log'
if ([System.IO.File]::Exists($logifile)) {
Clear-Content $logifile
Write-Host ("Logfile cleaned: $logifile")
} else {
try {
New-Item -Path . -Name $logifile | Out-Null
Write-Host ("New logfile created: $logifile")
}
catch {
"Failed to create $logifile"
}
}
foreach ($file in $file) {
if ($file.extension -eq '.JPG') { $jpg++ }
if ($file.extension -eq '.ARW') { $arw++ }
$i++
$datetaken = ($file.fullname | GetDateTaken).Split(' ')[0]
$datetaken_Day = $datetaken.Split('.')[0]
$datetaken_Month = $datetaken.Split('.')[1]
$datetaken_Year = $datetaken.Split('.')[2]
$TargetPath = "$copy_to$datetaken_Year\$datetaken_Month\$datetaken_Day\"
Write-Host ("$i. " + $file.Name + " `tDate taken: " + $datetaken)
robocopy $copy_from $TargetPath $file.Name /ts /fp /v /np /unilog+:$logifile | Out-Null
}
Write-Host ("`nTotal: " + $i + " files (" + $jpg + " JPG files, " + $arw + " ARW files)")
Not helps if write $TargetPath = $copy_to + $datetaken_Year + "\" + $datetaken_Month + "\" + $datetaken_Day + "\".
Not helps if I set /fat option to robocopy.
But, for example, when I set a year manualy, everything is ok $datetaken_Year = 2020
What should be fixed to create correct folder names?
Using the GetDetailsOf() method from the COM object returns localized results, which leads to your function on my Dutch machine returning the date in 'dd-MM-yyyy HH:mm' format (with invisible characters surrounding it).
A better approach IMO would be to get the date taken using System.Drawing.Imaging.Metafile to read the exif data as null-terminated byte array and parse the date from that as DateTime object using below function:
function Get-ExifDate {
# returns the 'DateTimeOriginal' property from the Exif metadata in an image file if possible
[CmdletBinding(DefaultParameterSetName = 'ByName')]
Param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0, ParameterSetName = 'ByName')]
[Alias('FullName', 'FileName')]
[ValidateScript({ Test-Path -Path $_ -PathType Leaf})]
[string]$Path,
[Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0, ParameterSetName = 'ByObject')]
[System.IO.FileInfo]$FileObject
)
Begin {
Add-Type -AssemblyName 'System.Drawing'
}
Process {
# the function received a path, not a file object
if ($PSCmdlet.ParameterSetName -eq 'ByName') {
$FileObject = Get-Item -Path $Path -Force -ErrorAction SilentlyContinue
}
# Parameters for FileStream: Open/Read/SequentialScan
$streamArgs = #(
$FileObject.FullName
[System.IO.FileMode]::Open
[System.IO.FileAccess]::Read
[System.IO.FileShare]::Read
1024, # Buffer size
[System.IO.FileOptions]::SequentialScan
)
try {
$stream = New-Object System.IO.FileStream -ArgumentList $streamArgs
$metaData = [System.Drawing.Imaging.Metafile]::FromStream($stream)
# get the 'DateTimeOriginal' property (ID = 36867) from the metadata
# Tag Dec TagId Hex TagName Writable Group Notes
# ------- --------- ------- -------- ----- -----
# 36867 0x9003 DateTimeOriginal string ExifIFD (date/time when original image was taken)
# get the date taken as an array of bytes
$exifDateBytes = $metaData.GetPropertyItem(36867).Value
# transform to string, but beware that this string is Null terminated, so cut off the trailing 0 character
$exifDateString = [System.Text.Encoding]::ASCII.GetString($exifDateBytes).TrimEnd("`0")
# return the parsed date
return [datetime]::ParseExact($exifDateString, "yyyy:MM:dd HH:mm:ss", $null)
}
catch{
Write-Warning -Message "Could not read Exif data from '$($FileObject.FullName)'"
}
finally {
If ($metaData) {$metaData.Dispose()}
If ($stream) {$stream.Close()}
}
}
}
Another option would be to download and unzip ExifTool
(you can download the zip files from here)
Then use it like:
$exifTool = 'Path\To\Unzipped\ExifTool.exe' # don't forget to 'Unblock' after downloading
$file = 'Path\To\The\ImageFile' # fullname
# retrieve all date tags in the file
# -s2 (or -s -s) return short tag name add the colon directly after that
$allDates = & $exifTool -time:all -s2 $file
# try to find a line with tag 'DateTimeOriginal', 'CreateDate' or 'ModifyDate'
# which will show a date format of 'yyyy:MM:dd HH:mm:ss'
# and parse a DateTime object out of this string
$dateTaken = switch -Regex ($allDates) {
'^(?:DateTimeOriginal|CreateDate|ModifyDate):\s(\d{4}:\d{2}:\d{2} \d{2}:\d{2}:\d{2})' {
[datetime]::ParseExact($matches[1], 'yyyy:MM:dd HH:mm:ss', $null)
break
}
}
Short explanation of what the above returns
Both methods return the date the image was taken as a DateTime object, not a string.
This object has properties like .Year, .Month, .Day etc. It also has various methods like .AddDays(), .ToShortDateString(), .ToString() and a lot more.
If you do $datetaken = ($datetaken -split ' ')[0] as per your comment, you are asking PowerShell to implicitely convert it to a string using the default ToString() method.
You can use that ToString() method in your code if you give it the formatting string you need in between the brackets, anyway you like.
If you for instance do $dateTaken.ToString('yyyy\\MM\\dd'), you'll get a string 2020\10\08 if $dateTaken was today, which could serve as part of a file path.
In your code, you could do:
$TargetPath = Join-Path -Path $copy_to -ChildPath $dateTaken.ToString('yyyy\\MM\\dd')
# if that path does not exist yet, create it
if (!(Test-Path -Path $TargetPath -PathType Container)) {
$null = New-Item -Path $TargetPath -ItemType Directory
}
Then go ahead and copy the file to the now existing $TargetPath
Please have a look at all the standard format strings and custom format specifiers you can use on a DateTime object.

download NuGet Licenses from VS Project and export it as plaintext

is there a way to convert html to plaintext?
I have a script that exports all NuGet-Licenses which been used in a visual studio project to a textfile.
Unfortunately the exports are mostly in HTML, and I found no way to solve it.
# Run in Package Manager Console with `./download-packages-license.ps1`.
# If access denied, execute `Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned`.
# Save licenses to One text file and one csv file instead of individual files
$LicensesFile = (Join-Path (pwd) 'licenses\Licenses.txt')
$LicensesFile_csv = (Join-Path (pwd) 'licenses\Licenses.csv')
$results = #()
# Below 2 lines to comment if you uncomment Split-Path ..
$solutionFile = "d:\Solutions\SolFile.sln"
cd "d:\Solutions"
# Uncomment below line if you wish to want to use above 2 lines
# Split-Path -parent $dte.Solution.FileName | cd;
New-Item -ItemType Directory -Force -Path ".\licenses";
#( Get-Project -All | ? { $_.ProjectName } | % {
Get-Package -ProjectName $_.ProjectName | ? { $_.LicenseUrl }
} ) | Sort-Object Id -Unique | % {
$pkg = $_;
Try
{
if ($pkg.Id -notlike 'microsoft*' -and $pkg.LicenseUrl.StartsWith('http'))
{
Write-Host ("Download license for package " + $pkg.Id + " from " + $pkg.LicenseUrl);
#Write-Host (ConvertTo-Json ($pkg));
$licenseUrl = $pkg.LicenseUrl
if ($licenseUrl.contains('github.com')) {
$licenseUrl = $licenseUrl.replace("/blob/", "/raw/")
}
$extension = ".txt"
if ($licenseUrl.EndsWith(".md"))
{
$extension = ".md"
}
(New-Object System.Net.WebClient).DownloadFile($licenseUrl, (Join-Path (pwd) 'licenses\') + $pkg.Id + $extension);
$licenseText = get-content "$((Join-Path (pwd) 'licenses\') + $pkg.Id + $extension)"
Remove-Item $((Join-Path (pwd) 'licenses\') + $pkg.Id + $extension) -ErrorAction SilentlyContinue -Force
$data = '' | select PkgId, LicenseText
$data.PkgId = $pkg.Id
$data.LicenseText = $licenseText | Out-String
$results += $data
# save in txt file
"Designation: NugetPackage $($pkg.Id)" | Add-Content $LicensesFile
$licenseText | Add-Content $LicensesFile
"" | Add-Content $LicensesFile
"" | Add-Content $LicensesFile
"" | Add-Content $LicensesFile
"" | Add-Content $LicensesFile
Write-Host "Package $($pkg.Id): License Text saved to $LicensesFile" -ForegroundColor Green
}
}
Catch [system.exception]
{
Write-Host ("Could not download license for " + $pkg.Id)
}
}
# save in .csv file
$results | Export-Csv $LicensesFile_csv -nti
Source of the Script here
A user also said ,,Unfortunately, most license URLs now point to HTML-only versions (early 2020). For example, licenses.nuget.org ignores any "Accept: text/plain" (or json) headers and returns html regardless"
So is there even a way to get the license information in plaintext?
Thanks and stay healthy!
So is there even a way to get the license information in plaintext?
Actually, we do not recommend that you convert the html file into plaintext format. And when you get the license data from nuget.org, it is the data returned from the site in full HTML format, which is designed by that.
The returned data also contains various formats for the license field, so we should not easily modify the accepted data format(such as plaintext ). And if possible, the only way to do this is to get rid of the HTML format fields from the source data, but however, it is impossible by Powershell and it cannot be done so far.
Therefore, in order to strictly follow the format of the returned data, it is best to use an HTML file to receive license info. It can maintain consistency with the website in the form of html.
Suggestion
1) change these in powershell:
$LicensesFile = (Join-Path (pwd) 'licenses\Licenses.html')
$LicensesFile_csv = (Join-Path (pwd) 'licenses\Licenses_csv.html')
And then you can get what you want.
Hope it could help you.

How can I add the device names that were not scanned (offline etc) by a PowerShell script

Very very much a PowerShell newbie here I wanted a script to scan devices on the network and report on Local Admins. Found one out there and made some minor modifications to meet my needs - but I have one mod I cant work out how to do. Hoping someone out there will know a simple way to do it ?
The scrip below will read in a list of device names - scan them and output a dated report for all devices that are live and on-line. If the device is not accessible I get the following error on screen but nothing in the report.
I would like when it encounters an error that it writes to the report file - something along the lines of "$computor was not accessible!"
The code I am using is
$date = Get-Date -Format o | foreach {$_ -replace ":", "."}
ECHO "Starting scan"
$Result = #()
foreach($server in (gc .\servers.txt)){
$computer = [ADSI](”WinNT://” + $server + “,computer”)
$Group = $computer.psbase.children.find(”Administrators”)
$Filename = "c:\" + "LocalAdminAudit" + $date + ".txt"
function getAdmins
{
ECHO "SEARCHING FOR DEVICE"
$members = ($Group.psbase.invoke(”Members”) | %
{$_.GetType().InvokeMember(”Adspath”, ‘GetProperty’, $null, $_, $null)}) -
replace ('WinNT://DOMAIN/' + $server + '/'), '' -replace ('WinNT://DOMAIN/',
'DOMAIN\') -replace ('WinNT://', '')
$members}
ECHO "READY TO WRITE OUTPUT"
$Result += Write-Output "SERVER: $server"
$Result += Write-Output ' '
$Result += ( getAdmins )
$Result += Write-Output '____________________________'
$Result += Write-Output ' '
ECHO "Record written"
}
# Added date run to report
$result += Write-Output "Date Reported: $date"
$Result > $Filename
Invoke-Item $Filename
# replace "DOMAIN" with the domain name.
ECHO "Scan Complete"
And the on screen error when a machine is off line or otherwise doesn't respond is
Exception calling "Find" with "1" argument(s): "The network path was not found.
"
At \server\users\User.Name\Powershell Scripts\Get-Local-AdminsV3.ps1:1
0 char:40
+ $Group = $computer.psbase.children.find <<<< (”Administrators”)
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
I would like when it encounters an error that it writes to the report file - something along the lines of "$computor was not accessible!" - I am pretty sure there must be an easy way of doing this - but I cant work it out so any tips would be greatly appreciated
As Matt, mentioned in the comments. You can use a Try/Catch block inside your function to catch the error.
I also made some other changes. The most major is that I changed the function to contain all of the code necessary to get the local administrator group. Then the loop just calls the function once per computer with the computer name. This function is then reusable.
Secondly rather than output to a text file, I changed to outputting to a CSV as is a more structured format that can be used better later.
Also rather than relying on writing to the console host, I used Write-Progress to report the progress of the loop.
$Servers = Get-Content .\servers.txt
$ExportFileName = "c:\LocalAdminAudit$date.csv"
function Get-LocalAdministrator {
[cmdletbinding()]
Param(
$ComputerName
)
$Group = [ADSI]("WinNT://$computername/Administrators,group")
try {
$Group.Invoke("Members") | ForEach-Object {
$User = ($_.GetType().InvokeMember("Adspath", 'GetProperty', $null, $_, $null) -split '/')[-2,-1] -join '\'
[PSCustomObject]#{
"User" = $User
"Server" = $ComputerName
"Date" = Get-Date -Format o | ForEach-Object {$_ -replace ":", "."}
}
}
}
catch {
[PSCustomObject]#{
"User" = "Failed to Report"
"Server" = $ComputerName
"Date" = Get-Date -Format o | ForEach-Object {$_ -replace ":", "."}
}
}
}
$LocalAdmins = foreach ($Server in $Servers) {
Write-Progress -Activity "Retrieving Local Administrators" -Status "Checking $Server" -PercentComplete (([array]::indexof($Servers,$Server)/($Server.count))*100)
Get-LocalAdministrator $Server
}
$LocalAdmins | Export-CSV $ExportFileName -NoTypeInformation
Invoke-Item $ExportFileName
Lastly, be careful of smart quotes especially when cutting and pasting between Outlook and word.

Resources