Pound sign in escaped powershell uri string - powershell

I'm using powershell to transfer data to my server.
I had an original question here: Powershell downloadFile with special characters about trying to escape a string inside of batch, which I had no luck with. So I created the short powershell script here:
$model = $args[0]
$os = $args[1]
$gpu = $args[2]
$hdd = $args[3]
$ram = $args[4]
$processor = $args[5]
$realmodel = $args[6]
$realupc = $args[7]
$productnumber = $args[8]
$url = [uri]::EscapeUriString("http://example.com/otherdb/modelfinder/form.php?model=$model&os=$os&graphics=$gpu&hdd=$hdd&ram=$ram&processor=$processor&actualmodel=$realmodel&Implement=0&UPC=$realupc&otherIdentifier=$productnumber")
(New-Object Net.WebClient).DownloadFile($url, 'save.txt')
Which successfully escapes the string and sends it to my server to be processed.
The problem is, near the end of my url, there is sometimes a pound sign # - which I believe powershell may be counting as a comment, or simply isn't being encoded. When checking my database after the url is sent, the # and everything after it is removed from the cell.
How can I encode the string to be sent exactly the way it is to be saved in my database?

You should escape your arguments separately as data strings. When you use [uri]::EscapeUriString("#") you will see that # will not be escaped.
PS > [uri]::EscapeUriString("#");
#
PS > [uri]::EscapeDataString("#")
%23
So as an abbreviated example, you could construct your string like this:
$sb = [System.Text.StringBuilder]::new();
$model = "# model with hash sign"
$os = "some operating system with exclamation mark!!";
$sb.Append("http://example.com/otherdb/modelfinder/form.php?model=");
$sb.Append([uri]::EscapeDataString($model).ToString());
$sb.Append("&os=");
$sb.Append([uri]::EscapeDataString($os).ToString());
$sb.ToString();
http://example.com/otherdb/modelfinder/form.php?model=%23%20model%20with%20hash%20sign&os=some%20operating%20system%20with%20exclamation%20mark!!

Related

give value into powershell script

I want to use a string inside a PowerShell script. It should be handed over like a variable when executing the script like this:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -c C:\temp\myscript.ps1 "string"
That is working well with just one word. But my string looks like this and should be handed over:
**<UserInputs><UserInput Question="Gruppenname" Answer="<Values Count="1"><Value DisplayName="Humanresources" Id="af05c5d3-2312-c897-8439-08979d4d0a49" /></Values>" Type="System.SupportingItem.PortalControl.InstancePicker" /><UserInput Question="Ausgabe" Answer="Namen" Type="richtext" /></UserInputs>**
This string contains some quotation marks and I have problems injecting it into my script.
inside the script I have this:
$mystring = $Null if($args[0] -ne $Null) { $mystring = $args[0] } $result = $mystring | Select-String -Pattern "DisplayName="(.*?)"" $result= $result.Matches.Groups[1] $group = $result.value Write-Output "$group" | Out-file C:\temp\group.txt
PowerShell scripts can take parameters, which become variables that are just available to the rest of the script. Use this at the top of your script to setup three variables, one boolean (true false) type, one string type and one integer type.
#stack.ps1
param(
# Param1 help description
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true,
Position=0)]
[string]$MyString="MyDefaultValue",
[int]$MyIntInput,
[bool]$MyTrueFalseInput
)
"Script running!"
"Values recieved!"
"MyString = $MyString"
"MyIntInput = $MyIntInput"
"MyTrueFalse = $MyTrueFalseInput"
Then to pass in values
C:\temp> .\stack.ps1 -MyString Ham -MyIntInput 75 -MyTrueFalseInput $true
Script running!
Values recieved!
MyString = Ham
MyIntInput = 75
MyTrueFalse = True
How to work with odd or complex strings
Now, to pass in a complex string that has quotes, use this syntax:
#using single quotes as a delimiter, ignoring the doubles inside.
$myWeirdString = 'ThisIs"SomeString"WhichHasQuotes"WhichIsWeird'
## extra case, if it's really odd and has both quote types
$myWeirdString = #"
'ThisIs"SomeString"WhichHasQuotes"WhichIsWeird'
"#

Powershell - Get partial name as Variable

First posting here, and I'm a beginner in Powershell (Or most script, in the matter of fact).
What a try to do here is to take a part of a name file, and use it later To download a file from our server with the same name.
$name = Get-childitem -Path "$env:USERPROFILE\AppData\Local\Microsoft\Outlook\*.ost" -name
$username = $name.Remove($name.IndexOf('#'))
Write-Output $Username
I only get the the file name without what I put between ${.}
Thanks for any help
Split the string on the # and grab only the first item from the resulting array:
$name = "FirstName.LastName#company.com - more text you don't care about.ost"
# String.Split() returns an array of strings, `[0]` gives us the first item in that array
$username = $name.Split('#')[0]
Alteratively, remove everything from the original string beginning at the first #:
$username = $name.Remove($name.IndexOf('#'))

MapNetworkDrive credential escaping special characters problem

I am actually mapping network drive with pgina at boot,
the only problem is i can't escape special characters like :"'
I use args[1] as input, but special characters like " transfoms into something else, i need to use them raw because i need to mount a user folder even with passwords with special characters.
$user = $args[0];
$password = $args[1];
$user = $user.Trim();
$user= $user.ToLower();
Write-Output "$user | $password" > c:\SCRIPTS\TMP\lastlog.txt;
$PSnet = $(New-Object -ComObject WScript.Network);
$smbserver = "172.18.49.101";
$userFolder="\\$smbserver\$user";
$PSnet.MapNetworkDrive("Z:", $userFolder, $false, "smbserver\$userid", $password;

creating a mapped drive from variables

I had all this working but must have changed something because it doesn't now! I want to pass in variables and then from them create a mapped drive.
$d = "O:";
$s = "\\Server\Folder";
$u = "/user:DOMAIN\UserId password";
net use $d $s $u;
When I run that now, powershell just hangs. I don't get any errors or nothing it just "runs" but doesn't complete. I have to hit the stop script button. If I check net use there is no entry for it in there.
if I manually type net use O: \\Server\Folder /user:DOMAIN\UserId password then it works and creates the mapped drive.
I have tried escaping the backslash as per below:
$d = "O:";$s = "\\\\Server\Folder";$u = "/user:DOMAIN\\UserId password";
also building the string like (with and without the escaped backslashes.):
$n = $d+' '+$s+' '+$u;
net use n;
this method advises the network cannot be found, again, with and without the escaped backslashes. How ever If I echo $n and paste the code it creates the drive so I am totally at a loss!
Calling a CMD command within a PowerShell script you should use Invoke-Expression.
$d = "O:";
$s = "\\Server\Folder";
$u = "/user:DOMAIN\UserId password";
Invoke-Expression "C:\Windows\System32\net.exe use $d $s $u"

Safe way to convert string literal without using Invoke-Expression [duplicate]

Imagine the following code:
# Script Start
$WelcomeMessage = "Hello $UserName, today is $($Date.DayOfWeek)"
..
..
# 100 lines of other functions and what not...
..
function Greet-User
{
$Username = Get-UserNameFromSomewhereFancy
$Date = Get-DateFromSomewhereFancy
$WelcomeMessage
}
This is a very basic example, but what it tries to show is a script where there is a $WelcomeMessage that the person running the script can set at the top of the script and controls how/what the message displayed is.
First thing's first: why do something like this? Well, if you're passing your script around to multiple people, they might want different messages. Maybe they don't like $($Date.DayOfWeek) and want to get the full date. Maybe they don't want to show the username, whatever.
Second, why put it at the top of the script? Simplicity. If you have 1000 lines in your script and messages like these spread all over the script, it makes it a nightmare for people to find and change these messages. We already do that for static messages, in the form of localized strings and stuff, so this is nothing new, except for the variable parts in it.
So, now to the issue. If you run that code and invoke Greet-User (assuming the functions/cmdlets for retrieving username and date actually exist and return something proper...) Greet-User will always return Hello , today is.
This is because the string is expanded when you declare it, at the top of the script, when neither $UserName nor $Date objects have a value.
A potential workaround would be to create the strings with single quotes, and use Invoke-Expression to expand them. But because of the spaces, that gets a bit messy. I.e.:
$WelcomeMessage = 'Hello $env:USERNAME'
Invoke-Expression $WelcomeMessage
This throws an error because of the space, to get it to work properly it would have to be declared as such:
$WelcomeMessage = 'Hello $env:USERNAME'
$InvokeExpression = "`"$WelcomeMessage`""
Messy...
Also, there's another problem in the form of code injection. Since we're allowing the user to write their own welcome message with no bounds specified, what's to prevent them from putting in something like...
$WelcomeMessage 'Hello $([void] (Remove-Item C:\Windows -Force -Recurse))'
(Yes, I know this will not delete everything but it is an example)
Granted this is a script and if they can modify that string they can also modify everything else on the script, but whereas the example I gave was someone maliciously taking advantage of the nature of the script, it can also happen that someone accidentally puts something in the string that ends up having unwanted consequences.
So... there's got to be a better way without the use of Invoke-Expression, I just can't quite thing of one so help would be appreciated :)
Embedding variables into strings is not the only way to create dynamic text, the way I would do it is like this:
$WelcomeMessage = 'Hello {0}, today is {1}'
# 100 lines of other functions and what not...
function Greet-User
{
$Username = Get-UserNameFromSomewhereFancy
$Date = Get-DateFromSomewhereFancy
$WelcomeMessage -f $Username, $Date
}
The canonical way to delay evaluation of expressions/variables in strings is to define them as single-quoted strings and use $ExecutionContext.InvokeCommand.ExpandString() later on.
Demonstration:
PS C:\> $s = '$env:COMPUTERNAME'
PS C:\> $s
$env:COMPUTERNAME
PS C:\> $ExecutionContext.InvokeCommand.ExpandString($s)
FOO
Applied to your sample code:
$WelcomeMessage = 'Hello $UserName, today is $($Date.DayOfWeek)'
...
...
...
function Greet-User {
$Username = Get-UserNameFromSomewhereFancy
$Date = Get-DateFromSomewhereFancy
$ExecutionContext.InvokeCommand.ExpandString($WelcomeMessage)
}
Have you considered using a lambda expression; i.e. instead of defining the variable as a string value define it as a function, then invoke that function passing the relevant parameters at runtime.
$WelcomeMessage = {param($UserName,$Date);"Hello $UserName, today is $($Date.DayOfWeek) $([void](remove-item c:\test\test.txt))"}
#...
# 100 lines of other functions and what not...
#...
"testfile" >> c:\test\test.txt #ensure we have a test file to be deleted
function Get-UserNameFromSomewhereFancy(){return "myUsername";}
function Get-DateFromSomewhereFancy(){return (get-date);}
function Greet-User
{
$Username = Get-UserNameFromSomewhereFancy
$Date = Get-DateFromSomewhereFancy
$WelcomeMessage.invoke($username,$date)
}
cls
Greet-User
Update
If you only wish to allow variable replacement the below code would do the trick; but this fails to do more advanced functions (e.g. .DayOfWeek)
$WelcomeMessage = 'Hello $Username, today is $($Date.DayOfWeek) $([void](remove-item c:\test\test.txt))'
#...
# 100 lines of other functions and what not...
#...
"testfile" >> c:\test\test.txt #ensure we have a test file to be deleted
function Get-UserNameFromSomewhereFancy(){return "myUsername";}
function Get-DateFromSomewhereFancy(){return (get-date);}
function Resolve-WelcomeMessage(){
write-output {param($UserName,$Date);"$WelcomeMessage";}
}
function Greet-User
{
$Username = Get-UserNameFromSomewhereFancy
$Date = Get-DateFromSomewhereFancy
$temp = $WelcomeMessage
get-variable | ?{#('$','?','^') -notcontains $_.Name} | sort name -Descending | %{
$temp = $temp -replace ("\`${0}" -f $_.name),$_.value
}
$temp
}
cls
Greet-User
Update
To avoid code injection this makes use of -whatif; that will only help where the injected code supports the whatif functionality, but hopefully better than nothing...
Also the code now doesn't require parameters to be declared; but just takes those variables which are available at the time of execution.
$WelcomeMessage = {"Hello $Username, today is $($Date.DayOfWeek) $([void](remove-item c:\test\test.txt))"}
#...
# 100 lines of other functions and what not...
#...
function Get-UserNameFromSomewhereFancy(){return "myUsername";}
function Get-DateFromSomewhereFancy(){return (get-date);}
function Resolve-WelcomeMessage(){
write-output {param($UserName,$Date);"$WelcomeMessage";}
}
"testfile" >> c:\test\test.txt #ensure we have a test file to be deleted
function Greet-User {
[cmdletbinding(SupportsShouldProcess=$True)]
param()
begin {$original = $WhatIfPreference; $WhatIfPreference = $true;}
process {
$Username = Get-UserNameFromSomewhereFancy
$Date = Get-DateFromSomewhereFancy
& $WelcomeMessage
}
end {$WhatIfPreference = $original;}
}
cls
Greet-User