Setup deployment group agent with powershell failed - powershell

I try to run the script provided in “deployment group” section on my Win7 VM (what I do is just “copy\paste\run”).
$ErrorActionPreference="Stop";
If(-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() ).IsInRole( [Security.Principal.WindowsBuiltInRole] “Administrator”)){ throw "Run command in an administrator PowerShell prompt"};
If($PSVersionTable.PSVersion -lt (New-Object System.Version("3.0"))){ throw "The minimum version of Windows PowerShell that is required by the script (3.0) does not match the currently running version of Windows PowerShell." };
If(-NOT (Test-Path $env:SystemDrive\'azagent')){mkdir $env:SystemDrive\'azagent'};
cd $env:SystemDrive\'azagent';
for($i=1; $i -lt 100; $i++){$destFolder="A"+$i.ToString();
if(-NOT (Test-Path ($destFolder))){mkdir $destFolder;cd $destFolder;break;}};
$agentZip="$PWD\agent.zip";
$DefaultProxy=[System.Net.WebRequest]::DefaultWebProxy;$securityProtocol=#();$securityProtocol+=[Net.ServicePointManager]::SecurityProtocol;
$securityProtocol+=[Net.SecurityProtocolType]::Tls12;[Net.ServicePointManager]::SecurityProtocol=$securityProtocol;
$WebClient=New-Object Net.WebClient;
$Uri='https://vstsagentpackage.azureedge.net/agent/2.204.0/vsts-agent-win-x64-2.204.0.zip';
if($DefaultProxy -and (-not $DefaultProxy.IsBypassed($Uri))){$WebClient.Proxy= New-Object Net.WebProxy($DefaultProxy.GetProxy($Uri).OriginalString, $True);};
$WebClient.DownloadFile($Uri, $agentZip);
Add-Type -AssemblyName System.IO.Compression.FileSystem;
[System.IO.Compression.ZipFile]::ExtractToDirectory( $agentZip, "$PWD");
.\config.cmd --deploymentgroup --deploymentgroupname "MY_GROUP_NAME" --agent $env:COMPUTERNAME --runasservice --work '_work' --url 'https://dev.azure.com/MY_ORG_NAME/' --projectname 'MY_PROJ_NAME';
Remove-Item $agentZip;
And error occurs:
em $agentZip;
At line:1 char:179
+ ... Current() ).IsInRole( [Security.Principal.WindowsBuiltInRole] “Adminis ...
+ ~
Missing ')' in method call.
At line:1 char:180
+ ... nRole( [Security.Principal.WindowsBuiltInRole] “Administrator?){ throw ...
+ ~~~~~~~~~~~~~~
Unexpected token '“Administrator?' in expression or statement.
At line:1 char:180
+ ... nRole( [Security.Principal.WindowsBuiltInRole] “Administrator?){ throw ...
+ ~~~~~~~~~~~~~~
Missing closing ')' after expression in 'If' statement.
At line:1 char:194
+ ... Role( [Security.Principal.WindowsBuiltInRole] “Administrator?){ throw ...
+ ~
Unexpected token ')' in expression or statement.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : MissingEndParenthesisInMethodCall
Here is the info of powershell version:
Any suggestion is appreciated.

As mentioned in the question comments, the issue stems around faulty quotation marks in the provided registration script.
It seems to only affect more modern Windows/PowerShell versions, so it hasn't been addressed by Microsoft with any priority yet. For now, the workaround is to find and replace.
If you paste the script into a text editor, search for “Administrator” and replace with "Administrator".
If you paste the script into (a modern) PowerShell prompt directly, the faulty characters will be stripped and you will need to manually add them:

Related

using powershell invoke-expression to run code output

I have been doing a lot of reading on invoke-expression (also known as iex) and I'm having trouble getting it to work for me.
My understanding is, it will run any powershell code you give to it. However, when I run my tests on it, it does not run the code.
Example:
## testcode.ps1
$myvar = "i am here"
if ($myvar -ne $null) {
"($myvar) variable is Full"
} else {
"($myvar) variable is Empty"
}
Now, if I cat(gc) this file and I pass it to iex, it outputs a bunch of errors. Same thing happens when I save the code into a variable and then feed the variable to iex. Neither works.
Despite the fact that I've tried numerous examples, I feel there's something minor I'm doing wrong that I'm hoping someone can point out for me.
I'm new to Windows scripting, so please bear with me. These are the results of the tests I performed:
First Test:
PS C:\Users\J> gc C:\Users\J\testcode.ps1 | iex
Invoke-Expression : Cannot bind argument to parameter 'Command' because it is an empty string.
At line:1 char:31
+ cat C:\Users\J\testcode.ps1 | iex
+ ~~~
+ CategoryInfo : InvalidData: (:PSObject) [Invoke-Expression], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAllowed,Microsoft.PowerShell.Commands.InvokeExpressionCommand
iex : At line:1 char:23
+ if ($myvar -ne $null) {
+ ~
Missing closing '}' in statement block or type definition.
At line:1 char:31
+ cat C:\Users\J\testcode.ps1 | iex
+ ~~~
+ CategoryInfo : ParserError: (:) [Invoke-Expression], ParseException
+ FullyQualifiedErrorId : MissingEndCurlyBrace,Microsoft.PowerShell.Commands.InvokeExpressionCommand
Second Test:
PS C:\Users\J> $scriptBlock = gc C:\Users\J\testcode.ps1
PS C:\Users\J>
PS C:\Users\J> iex -Command "$scriptBlock"
iex : At line:1 char:23
+ $myvar = "i am here" if ($myvar -ne $null) { "($myvar) variable ...
+ ~~
Unexpected token 'if' in expression or statement.
At line:1 char:1
+ iex -Command "$scriptBlock"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ParserError: (:) [Invoke-Expression], ParseException
+ FullyQualifiedErrorId : UnexpectedToken,Microsoft.PowerShell.Commands.InvokeExpressionCommand
PS C:\Users\J>
I'm aware that I can just run the file containing the code. However, I need help figuring out how iex works and what it is I'm doing wrong.
Please kindly advise.
First things first:
Invoke-Expression should generally be avoided and used only as a last resort, due to its security risks. In short: avoid it, if possible, given that superior alternatives are usually available. If there truly is no alternative, only ever use it on input you either provided yourself or fully trust - see this answer.
For the record: in the case at hand, the superior alternative is to directly invoke the script file:
# Prepend `& `, if the script file path is quoted or references a variable.
C:\Users\J\testcode.ps1
Invoke-Expression (iex) accepts multiple strings via the pipeline, and evaluates each individually, as a self-contained script.
Therefore, you must provide the contents of your script as a whole, as a single string, which is what Get-Content's (gc's) -Raw switch does[1]:
Get-Content -Raw C:\Users\J\testcode.ps1 | Invoke-Expression
Alternatively, pass the script-file contents as an argument:
Invoke-Expression (Get-Content -Raw C:\Users\J\testcode.ps1)
Note that passing the string to evaluate as an argument truly only accepts a single string, so the command would fail without -Raw.
[1] By default, the Get-Content cmdlet reads a file line by line, passing each line through the pipeline as it is being read.
$myvar = "I'm Here"
#Using Invoke-Expression - Accepts a STRING as Input
$SBCode = 'if ($Null -ne $myvar) {"($myvar) variable is Full"}' +
'else {"`$myvar variable is Empty"}'
Clear-Host
"Before Invoke-Expression `$myvar = $myvar"
$Result = Invoke-Expression $SBCode
"Invoke-Expression Returns: $Result"
#Using Invoke-Command - Accepts Script Block as Input
$SBCode = {
if ($myvar -ne $null) {
"($myvar) variable is Full"
}
else {
"`$myvar variable is Empty"
}
} #End $SBCode Script Block
"Before Invoke-Command `$myvar = $myvar"
$Result = Invoke-Command -ScriptBlock $SBCode
"Invoke-Command Returns: $Result"
Results:
Before Invoke-Expression $myvar = I'm Here
Invoke-Expression Returns: (I'm Here) variable is Full
Before Invoke-Command $myvar = I'm Here
Invoke-Command Returns: (I'm Here) variable is Full
# After changing $MyVar = $Null
Before Invoke-Expression $myvar =
Invoke-Expression Returns: $myvar variable is Empty
Before Invoke-Command $myvar =
Invoke-Command Returns: $myvar variable is Empty
HTH
You can use out-string to convert output into string.
cat C:\Users\J\testcode.ps1 | out-string | Invoke-Expression

System.IO.Compression.ZipFileExtensions - CreateEntryFromFile giving "because it is being used by another process" on zip

I am trying to compress 1 file into a new archive. The below code works.... most of the time. However, once in a while (network issues?) I changed the code (see below) to create a unique name, but I still occasionally get this error.
Is there any way to work around this problem? I'm on powershell 3 so compress-archive isn't an option, though I can probably get pscx installed.
Exception calling "Open" with "2" argument(s): "The process cannot access the file '\\path\share$\folder\filename_20200221_14__20200224_102143.zip' because it is being used by another process."
At line:3 char:5
+ [System.IO.Compression.ZipArchive]$ZipFile = [System.IO.Compression.ZipFile] ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : IOException
Here's the code I'm using.
I'm doing a "foreach ($file in $files_to_process)", but was able to duplicate the error by using a foreach {} to set variables, then run the below code. Subsequent runs are working, naturally.
$timestamp = (get-date).ToString("yyyyMMdd_HHmmss")
# Now create the zip file and remove the old one
[string]$zipFN = "$OnPremDirectoryDone$($file.basename)__$($timestamp).zip"
[string]$fileToZip = "$OnPremDirectory$($file.name)"
[System.IO.Compression.ZipArchive]$ZipFile = [System.IO.Compression.ZipFile]::Open($zipFN, ([System.IO.Compression.ZipArchiveMode]::Update))
[System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($ZipFile, $fileToZip, (Split-Path $fileToZip -Leaf))
$ZipFile.Dispose()
remove-item "$fileToZip"

Compare select-string piped result against a string

I want to compare grep a like Select-String result against a string and return a Boolean true or false, but I can't match the right syntax when I put everything in the if/else expressions.
The error output that I get is:
At line:1 char:102
+ ... Object {$_ | Select-String 1 packages found.}) -eq 1 packages found.) ...
+ ~~~~~~~~ Unexpected token 'packages' in expression or statement. At line:1
char:102
+ ... Object {$_ | Select-String 1 packages found.}) -eq 1 packages found.) ...
+ ~~~~~~~~ Missing closing ')' after expression in 'if' statement. At line:1
char:117
+ ... $_ | Select-String 1 packages found.}) -eq 1 packages found.) { echo ...
+ ~ Unexpected token ')' in expression or statement. At line:1 char:184
+ ... rometheus-wmi-exporter is already installed, skipping. } else { c:/pr ...
+ ~~~~ Unexpected token 'else' in expression or statement.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : UnexpectedToken
If I run the contents of the first expression I get the result as I expect it, but I fail to return a Boolean true or false when I put it in the expression part of the if.
powershell -NoProfile -ExecutionPolicy unrestricted -Command --% "if ((choco list prometheus-wmi-exporter | Where-Object {$_ | Select-String "1 packages found."}) -eq "1 packages found.") { echo "prometheus-wmi-ex
porter is already installed, skipping." } else { c:/programdata/chocolatey/bin/choco.exe install --force -y prometheus-wmi-exporter.install }"
My end goal is if I get a match to return true, else to return false and execute the false statement.
your command doesn't check what you expect, it just retuns if such a package exists,
not if it is installed -local
the question is tagged [powershell] but you invoke powershell? Is this supposed to run from a batch? If so why not just use clist.exe (shortcut for choco list) and find to check?
clist -local|find /i "prometheus-wmi-exporter" &&(echo installed)||(echo not installed)
the command is overly complex, an if with choco output checked with a -(not)match should suffice.
if((clist -local) -match 'prometheus-wmi-exporter'){"installed"}else{"not installed"}
before wrapping the command test it directly
Try rewriting like this:
powershell -NoProfile -Command "if (choco prometheus-wmi-exporter | Select-String '1 packages found.') { echo 'prometheus-wmi-exporter is already installed, skipping.' } else { c:/programdata/chocolatey/bin/choco.exe install --force -y prometheus-wmi-exporter.install }"

Add folder to zip

I am facing a problem how to add folder to existing ZIP file.
This zip file is created by PowerShell also.
I can only use system classes provided by Powershell 5. I cannot use any of user packages or plugins (7zip included).
Here is my code:
function addFileToArchiveTest ($filePathToAdd, $archivePathToUpdate) {
if ([System.IO.File]::Exists($filePathToAdd) -or (Test-Path $filePathToAdd)) {
$file = [System.IO.Path]::GetFileName($filePathToAdd);
Write-Host $filePathToAdd.Name;
Write-Host $filePathToAdd;
Write-Host $archivePathToUpdate;
$archive = [System.IO.Compression.ZipFile]::Open($archivePathToUpdate, [System.IO.Compression.ZipArchiveMode]::Update);
$compressionLevel = [System.IO.Compression.CompressionLevel]::NoCompression;
[System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($archive, $filePathToAdd, $file, "$compressionLevel");
$archive.Dispose();
} else {
Write-Host "[ERROR#function] <AddFileToArchive>: <filePathToAdd> does not exist!";
Write-Host "[ERROR#function] <Variable<filePathToAdd>>: $filePathToAdd";
Write-Host "[ERROR#function] <Variable<archivePathToUpdate>>: $archivePathToUpdate";
}
}
I am thinking about variable $file - there might be a problem, because folder doesn't have an extension.
I run script like this:
PS> addFileToArchiveTest "C:\TestFolder\FolderToArchive" "C:\TestFolder\thereIsAlreadyZipFile.zip"
It returns with error:
Exception calling "CreateEntryFromFile" with "4" argument(s): "Access to the
path 'C:\TestFolder\FolderToArchive' is denied."
At C:\Users\user\Desktop\testfolder.ps1:196 char:13
+ [System.IO.Compression.ZipFileExtensions]::CreateEntryFro ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : UnauthorizedAccessException
Noted I also try allow script and I am launching with admin rights.
Perhaps surprisingly, CreateEntryFromFile() is for adding files, not folders. You need to add each file individually:
Get-ChildItem $filePathToAdd | ForEach-Object {
[IO.Compression.ZipFileExtensions]::CreateEntryFromFile($archive, $_.FullName, $_.Name, "$compressionLevel")
}
As user #guiwhatsthat answered: PowerShell 5 does support Compress-Archive. It does exactly what you want.
That is working as I want.

Powershell v2.0 Foreach loop failing when run on PS v4

Hi is there a difference in the way that PS v4 (running from a 2012 server) reacts to a Foreach loop written in V2?
I have this simple script that works in v2 but produces an error on v4:
$Serverlist = Import-CSV "D:\PendingReboot\AllADServers.csv"
Foreach ($Computer.Name in $Serverlist)
{
$ADComputer = $Computer.Name
$ADOwner = $Computer.Description
If (!($Computer.Description))
{$ADOwner = "Unassigned"}
$ADOU = $AD | select DistinguishedName
"$ADComputer $ADOwner"
}
Server1 Team1
Server2 Team2
etc. etc.
The Error in V4 is:
At D:\PendingReboot\Test.ps1:2 char:23
+ Foreach ($Computer.Name in $Serverlist)
+ ~
Missing 'in' after variable in foreach loop.
At D:\PendingReboot\Test.ps1:2 char:43
+ Foreach ($Computer.Name in $Serverlist)
+ ~
Unexpected token ')' in expression or statement.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : MissingInInForeach
PetSerAl's comment is correct. You should be using foreach ($Computer in $ServerList). As for why it works in PowerShell v2 and not in v4, well I can only assume that they corrected the behavior, as it should have failed in v2.
Basically your .Name in that portion is doing nothing, as evidenced by the fact that you still needed to specify .Name on the very first line within the loop.
Essentially, this was a syntax error that wasn't caught in an earlier version.