I have an import tool that has support for running in unattended mode. It accepts arguments like this:
importer.exe -organization "DEV" -dataFile "E:\importData.txt" -rightsFile "E:\importRights.txt" -logFile "C:\LogFile.log"
Now above is how the tool itself accepts the arguments.
I'm writing a ps-script to launch the tool with the above parameters.
<#
.SYNOPSIS
Executes the PWR Bulk Data Importer tool in unattended mode.
.DESCRIPTION
This script executes the PWR Bulk Data Importer tool in unattended mode. The Import files: Data and rights must be supplied and log file must also be provided.
.PARAMETER ImportToolExe
The full path and exe of the tool.
.PARAMETER Organization
The identifier of the organization.
.PARAMETER DataFile
The full path and filename of data file.
.PARAMETER RightsFile
The full path and filename of rights file.
.PARAMETER LogFile
The full path and filename of log file (will be created and if already exists, it'll be over-writtien).
.PARAMETER IsForced
If true, tool will run in override mode omitting all deletion warnings.
.EXAMPLE
Executer -ImportToolExe "D:\tool\PWR Bulk Data Importer.exe" -Organization "VTDEV" -DataFile "E:\importData.txt" -RightsFile "E:\importRights.txt" -LogFile "C:\LogFile.log"
Executer -ImportToolExe "D:\tool\PWR Bulk Data Importer.exe" -Organization "VTDEV" -DataFile "E:\importData.txt" -RightsFile "E:\importRights.txt" -LogFile "C:\LogFile.log" -IsForced true
#>
param(
[Parameter(Mandatory = $true, Position = 0, ValueFromPipelineByPropertyName = $true)]
[string]$ImportToolExe,
[Parameter(Mandatory = $true, Position = 1, ValueFromPipelineByPropertyName = $true)]
[string]$Organization,
[Parameter(Mandatory = $true, Position = 2, ValueFromPipelineByPropertyName = $true)]
[string]$DataFile,
[Parameter(Mandatory = $true, Position = 3, ValueFromPipelineByPropertyName = $true)]
[string]$RightsFile,
[Parameter(Mandatory = $true, Position = 4, ValueFromPipelineByPropertyName = $true)]
[string]$LogFile,
[Parameter(Mandatory = $false)]
[bool]$IsForced
)
Write-Output ""
Write-Output "Script to execute Bulk Data Importer"
Write-Output ""
$params = "-organization " + $Organization + " -dataFile " + $DataFile + " -rightsFile " + $RightsFile + " -logFile " + $LogFile
Write-Output "Debuging"
Write-Output ($ImportToolExe + " " + $params)
Try
{
Write-Output " "
Write-Output "Executing..."
Invoke-Expression ($ImportToolExe + " " + $params)
Write-Output "Finished."
Write-Output "Checking exit code."
}
Catch [system.exception]
{
" "
"Exception while trying to execute"
Write-Output $_.Exception.GetType().FullName;
Write-Output $_.Exception.Message;
return
}
Finally
{
Write-Output " "
}
$IsImportSuccess = $false
IF ($lastexitcode -eq 0)
{
Write-Output "Import successful."
$IsImportSuccess = $true
}
ELSE
{
Write-Output "Import failed."
$IsImportSuccess = $false
}
IF ($IsImportSuccess -eq $true)
{
Try
{
$SMTPServer = "smtp.gmail.com"
$SMTPClient = New-Object Net.Mail.SMTPClient( $SmtpServer, 587 )
$SMTPClient.EnableSSL = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential( "GMAIL_USERNAME", "GMAIL_PASSWORD" );
$emailMessage = New-Object System.Net.Mail.MailMessage
$emailMessage.From = $EmailFrom
foreach ( $recipient in $Arry_EmailTo )
{
$emailMessage.To.Add( $recipient )
}
$emailMessage.Subject = $EmailSubj
$emailMessage.Body = $EmailBody
# Do we have any attachments?
# If yes, then add them, if not, do nothing
# if ( $Arry_EmailAttachments.Count -ne $NULL )
# {
# $emailMessage.Attachments.Add()
# }
$emailMessage.Attachments.Add($LogFile)
$SMTPClient.Send( $emailMessage )
}
Catch [system.exception]
{
" "
"Exception while emailing"
write-host $_.Exception.GetType().FullName;
write-host $_.Exception.Message;
return
}
}
No I'm getting output with errors:
Script to execute Bulk Data Importer
Debuging
D:\tool\PWR Bulk Data Importer.exe -organization VTDEV -dataFile E:\importData.txt -rightsFile E:\importRights.txt -logFile C:\LogFile.log
Executing...
Exception while trying to execute
System.Management.Automation.CommandNotFoundException
The term 'D:\tool\PWR' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
I'm facing a bit of learning curve with PS and had this script up by reading from SO so far.
I see the main issue where I print a debug line: All my quotes are gone already. And my manager told me that the Invoke-Expression is not a good idea. He recommends me to use Start-something
Now I'm stuck. Any pointer will be greatly appreciated and there will be upvotes as well.
Add -ErrorAction Stop inside try/catch, this should trigger desired action:
try{
...
Invoke-Expression ($ImportToolExe + " " + $params) -ErrorAction Stop
...
}
catch{
## catch code
}
You can also try Invoke-Command to start the exe so in your scenario replace Invoke-Expression with the following:
Invoke-command -ScriptBlock {&$ImportToolExe $args[0]} -ArgumentList $params
Related
I have a files in my SharePoint site that look something like this
ShardReportsFull/ShardData-Full-2022-03-28-03.00.12AM.csv
ShardReportsFull/ShardData-Full-2022-03-27-53.00.12AM.csv
ShardReportsFull/ShardData-Full-2022-03-25-34.00.12AM.csv
I'm just wondering how can I download the latest file. I have tried passing a date like this but the problem is the uploaded file name format. it has a time where is not consistent so I can't just pass a date like this so I need to find a way to download the latest file instead.
$Date = Get-Date
$ShardDate = $Date.ToString("yyyy-MM-dd")
$Global:ShardListURL = "/sites/msteams_88c7ed/ShardReportsFull/ShardData-Full-"+$ShardDate+".csv"
$Global:shardListCSV = "C:\scripts\Re-LitHold-OneDrive\Download-Files\ShardData-Full-"+$ShardDate+".csv"
$Global:SiteURL = "https://company.sharepoint.com/sites/"
$Global:ShardListURL = "/sites/msteams_88c7ed/ShardReportsFull/ShardData-Full-2022-03-27-03.00.11AM.csv"
$Global:shardListCSV = "C:\scripts\OneDrive\Download-Files\ShardData-Full-2022-03-27-03.00.11AM.csv"
Function Download-FileFromLibrary() {
param
(
[Parameter(Mandatory = $true)] [string] $SiteURL,
[Parameter(Mandatory = $true)] [string] $SourceFile,
[Parameter(Mandatory = $true)] [string] $TargetFile
)
Try {
#Setup Credentials to connect
$Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Global:adminUPN, $Global:adminPwd)
#Setup the context
$Ctx = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL)
$Ctx.Credentials = $Credentials
#sharepoint online powershell download file from library
$FileInfo = [Microsoft.SharePoint.Client.File]::OpenBinaryDirect($Ctx, $SourceFile)
$WriteStream = [System.IO.File]::Open($TargetFile, [System.IO.FileMode]::Create)
$FileInfo.Stream.CopyTo($WriteStream)
$WriteStream.Close()
Write-host -f Green "File '$SourceFile' Downloaded to '$TargetFile' Successfully!" $_.Exception.Message
}
Catch {
write-host -f Red "Error Downloading File!" $_.Exception.Message
}
}
Download-FileFromLibrary -SiteURL $Global:SiteURL -SourceFile $Global:ShardListURL -TargetFile $Global:shardListCSV
Can anyone advise on how to validate credentials on a remote domain?
My environment has multiple domains that do not have trust relationships defined between them.
I have a Powershell script that needs to access a shared folder residing on a server in another domain which obviously requires authentication. Prior to accessing it, I need to validate credentials to avoid lock-outs (The script can be ran against multiple servers).
In the past I've used this wonderful script which used current domain for validation but I cannot get it to work against a remote domain.
I tried this is (slightly modified script from link above):
function Test-Cred {
[CmdletBinding()]
[OutputType([String])]
Param (
[Parameter(
Mandatory = $false,
ValueFromPipeLine = $true,
ValueFromPipelineByPropertyName = $true
)]
[Alias(
'PSCredential'
)]
[ValidateNotNull()]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
$Credentials
)
$Domain = $null
$Root = $null
$Username = $null
$Password = $null
If($Credentials -eq $null)
{
Try
{
$Credentials = Get-Credential "domain\$env:username" -ErrorAction Stop
}
Catch
{
$ErrorMsg = $_.Exception.Message
Write-Warning "Failed to validate credentials: $ErrorMsg "
Pause
Break
}
}
# Checking module
Try
{
# Split username and password
$Username = $credentials.username
$Password = $credentials.GetNetworkCredential().password
# Get Domain
###$Root = "LDAP://" + ([ADSI]'').distinguishedName
$Root = "LDAP://DC=remote_domain,DC=com" ### statically define the remote domain
$Domain = New-Object System.DirectoryServices.DirectoryEntry($Root,$UserName,$Password)
}
Catch
{
$_.Exception.Message
Continue
}
If(!$domain)
{
Write-Warning "Something went wrong"
}
Else
{
If ($domain.name -ne $null)
{
return "Authenticated"
}
Else
{
$Domain ### diagnosing the error
return "Not authenticated"
}
}
}
I get the following error:
format-default : The following exception occurred while retrieving member "distinguishedName": "The user name or
password is incorrect.
"
+ CategoryInfo : NotSpecified: (:) [format-default], ExtendedTypeSystemException
+ FullyQualifiedErrorId : CatchFromBaseGetMember,Microsoft.PowerShell.Commands.FormatDefaultCommand
The username/password is 100% correct.
Thank you
EDIT 1
I have found the following blog post that goes over how to work with Active Directory using .Net assemblies. The following has worked quite well
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
#store credentials (of account with appropriate permissions)
$creds = Get-Credential
#set the domain name
$dn = 'contoso.com'
#Create the principal context object (so to say connect to a domain with the credentials provided)
$pc = [System.DirectoryServices.AccountManagement.PrincipalContext]::new([System.DirectoryServices.AccountManagement.ContextType]::`
Domain,$dn,$($creds.UserName),$($creds.GetNetworkCredential().Password))
I assume I could use this in an If statement to achieve what I need. Admittedly, I do not know the way of the .Net and it is a bit scary but I will have to learn it.
EDIT 2
Here is what I pieced together:
Function Test-Cred
{
[CmdletBinding()]
[OutputType([String])]
Param (
[Parameter(
Mandatory = $false,
ValueFromPipeLine = $true,
ValueFromPipelineByPropertyName = $true
)]
[Alias(
'PSCredential'
)]
[ValidateNotNull()]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
$Credentials
)
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
# Checking module
$Validated = [System.DirectoryServices.AccountManagement.PrincipalContext]::new([System.DirectoryServices.AccountManagement.ContextType]::Domain,'remote_domain',$($Credentials.UserName),$($Credentials.GetNetworkCredential().Password))
If ($Validated.ConnectedServer)
{
Return "Authenticated"
}
Else
{
Return "Not authenticated"
}
}
Any feedback?
EDIT 3
Well, EDIT 2 does not work for Powershell 4, grrr
Method invocation failed because [System.DirectoryServices.AccountManagement.PrincipalContext] dies not contain method named 'new'
I had to make it work like this:
$ContextType = [System.DirectoryServices.AccountManagement.ContextType]::Domain
$ContextName = 'target_domain.com'
$Validated = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList $ContextType, $ContextName, $($Credentials.UserName),$($Credentials.GetNetworkCredential().Password)
Here is my final version of this test function that works with Powershell version older than 5.1.
Function Test-Cred
{
[CmdletBinding()]
[OutputType([String])]
Param (
[Parameter(
Mandatory = $false,
ValueFromPipeLine = $true,
ValueFromPipelineByPropertyName = $true
)]
[Alias(
'PSCredential'
)]
[ValidateNotNull()]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
$Credentials
)
# Checking module
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$ContextType = [System.DirectoryServices.AccountManagement.ContextType]::Domain
$ContextName = 'remote_domain.com'
$Validated = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList $ContextType, $ContextName, $($Credentials.UserName),$($Credentials.GetNetworkCredential().Password)
If ($Validated.ConnectedServer)
{
Return "Authenticated"
}
Else
{
Return "Not authenticated"
}
}
Powershell password protected file
Hi, I'm new to powershell and try to learn some trick with it.
I created a simple code that's supposed to unzip a file using 7zip and a known password.
Here's the code:
$7ZipPath = '"C:\Program Files\7-Zip\7z.exe"'
$zipFile = '"C:\Users\touff\OneDrive\Bureau\0\Encrypted Zip\test.zip"'
$path = 'C:\Users\touff\OneDrive\Bureau\0\Encrypted Zip\foldertest'
New-Item -ItemType directory -Path $path
Read-Host -Prompt 'step1'
$password = Read-Host -Prompt 'Input the password'
Write-Host $password
$command = "& $7ZipPath e -oC:\ -y -tzip -p$password $zipFile"
Invoke-Expression $command
I keep getting these errors :
7-Zip [64] 16.04 : Copyright (c) 1999-2016 Igor Pavlov : 2016-10-04
Scanning the drive for archives:
1 file, 310 bytes (1 KiB)
Extracting archive: C:\Users\touff\OneDrive\Bureau\0\Encrypted Zip\test.zip
Path
= C:\Users\touff\OneDrive\Bureau\0\Encrypted Zip\test.zip
Type = zip
Physical Size = 310
ERROR: Can not open output file : Accès refusé. :
C:\ok.txt
Sub items Errors: 1
Archives with Errors: 1
Sub items Errors: 1
So here is what you are doing in function form to clean it up a bit
Function Open-7ZipFile{
Param(
[Parameter(Mandatory=$true)]
[string]$Source,
[Parameter(Mandatory=$true)]
[string]$Destination,
[string]$Password,
[Parameter(Mandatory=$true)]
[string]$ExePath7Zip,
[switch]$Silent
)
$Command = "& `"$ExePath7Zip`" e -o`"$Destination`" -y" + $(if($Password.Length -gt 0){" -p`"$Password`""}) + " `"$Source`""
If($Silent){
Invoke-Expression $Command | out-null
}else{
"$Command"
Invoke-Expression $Command
}
}
And here is how to run it
Open-7ZipFile -ExePath7Zip "C:\Program Files\7-Zip\7z.exe" -Source "C:\Users\touff\OneDrive\Bureau\0\Encrypted Zip\test.zip" -Destination "C:\Users\touff\OneDrive\Bureau\0\Encrypted Zip\foldertest" -Password "Password"
Make sure you have access to the folder you are trying to unzip to
If you do not have rights you will end up with the error you are getting now
ERROR: Can not open output file : Accès refusé. : C:\ok.txt
Edited the function to allow spaces and run silently.
You don't need Invoke-Expression; just run the command with the parameters you need. Here's a short sample script you can use (modify to suit your needs):
param(
[Parameter(Mandatory = $true)]
[String]
$ArchiveFilename,
[String]
$DestinationPath,
[Switch]
$HasPassword
)
$ARCHIVE_TOOL = "C:\Program Files\7-Zip\7z.exe"
function ConvertTo-String {
param(
[Security.SecureString] $secureString
)
try {
$bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureString)
[Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr)
}
finally {
if ( $bstr -ne [IntPtr]::Zero ) {
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)
}
}
}
if ( $HasPassword ) {
$securePwd = Read-Host -AsSecureString -Prompt "Enter archive password"
if ( $securePwd ) {
$password = ConvertTo-String $securePwd
}
}
if ( -not $DestinationPath ) {
$DestinationPath = (Get-Location).Path
}
& $ARCHIVE_TOOL e "-o$DestinationPath" "-p$password" $ArchiveFilename
If the script is named Expand-ArchiveFile.ps1, run it like this:
Expand-ArchiveFile.ps1 "C:\Users\touff\OneDrive\Bureau\0\Encrypted Zip\test.zip" -HasPassword
Note that when specifying filenames, you do not need the embedded quotes. (The quotes are not part of the file's name.)
I have a script, below, which I have in a Scripts folder. It references a set of command prompt applications $cmd1.exe, etc.. Using either Powershell or the Integrated Powershell terminal in VS Code, I follow these steps to use it:
Dot source the script from the Scripts directory > .\new-customconfig.ps1,
Change to the working directory > cd [New Directory],
Use the function in the script and pass a filename from the working directory > New-CustomConfig .\FileName.
It passes the file from the working directory the first time I run the script, but from any subsequent runs, it looks for the file in the Scripts directory.
Is there something I am doing wrong?
Function New-CustomConfig {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[string]
$FileName
)
Begin {
#Set tools directory
$ToolD = '[Directory]\Tools'
#Clean up filename
$FileName = $FileName -replace '^[\W]*', ''
#Setup Executables
$cmdtools = "\cmd1.exe", "\cmd2.exe", "\cmd3.exe"
#Setup Arguments
$cmdargs = "cmd1args", "cmd2args", "cmd3args"
#Setup Write-Host Comments
$cmdecho = "echo1", "echo2", "echo3"
#Setup command object info
$cmdinfo = New-Object System.Diagnostics.ProcessStartInfo
$cmdinfo.RedirectStandardError = $true
$cmdinfo.RedirectStandardOutput = $true
$cmdinfo.UseShellExecute = $false
#Create command object
$cmd = New-Object System.Diagnostics.Process
"Generating Config for $FileName"
}
Process {
for ($i = 0; $i -le $cmdtools.Count; $i++) {
$cmdinfo.FileName = $ToolD + $cmdtools[$i]
$cmdinfo.Arguments = '' + $cmdargs[$i] + ''
Write-Host $i
Write-Host $cmdinfo.FileName
Write-Host $cmdinfo.Arguments
Write-Host $(Get-Location)
$cmdecho[$i]
$cmd.StartInfo = $cmdinfo
$cmd.Start() | Out-Null
$cmd.WaitForExit()
$stdout = $cmd.StandardOutput.ReadToEnd()
$stderr = $cmd.StandardError.ReadToEnd()
Write-Host "stdout: $stdout"
Write-Host "stderr: $stderr"
Write-Host "exit code: " + $p.ExitCode
}
}
End {
"Press any key to continue..."
$null = $host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
}
}
I want to invoke a command line process, lets say that process accepts user input like "Press enter to exit".
How to invoke suck kind of process and get the return value from that process?
The above code crashes in that scenario in Powershell?
function InvokeProcess
{
param (
[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]$ExeName,
[parameter(Mandatory=$false)]
[ValidateNotNullOrEmpty()]$Arguments
)
try
{
[string] $ExecutablePath = $Path + $ExeName
$FullPath = [string] $ExecutablePath + $Arguments
$result = & $FullPath
}
catch
{
$Msg = "Exception: $error[0]"
WriteLog $Msg "Error"
}
}
$result = & $ExecutablePath $arg1 $arg2