Following this post when I am trying to implement Using functionality in Powershell
Function cUsing {
param (
[System.IDisposable] $inputObject = $(throw "The parameter -inputObject is required."),
[ScriptBlock] $scriptBlock = $(throw "The parameter -scriptBlock is required.")
)
Try { &$scriptBlock }
Finally {
if ($inputObject -ne $null) {
if ($inputObject.psbase -eq $null) {
$inputObject.Dispose()
} else {
$inputObject.psbase.Dispose()
}
}
}
}
cUsing($sqlConnection = New-Object System.Data.SqlClient.SqlConnection)
{
$sqlConnection.ConnectionString = "Server=myserver; Database=master; Integrated Security= True"
$sqlConnection.Open()
$sqlCmd = New-Object System.Data.SqlClient.SqlCommand
$sqlCmd.CommandText = "SELECT STATEMENT"
$sqlCmd.Connection = $sqlConnection
$dsValues = New-Object System.Data.DataSet
$daValues = New-Object System.Data.SqlClient.SqlDataAdapter($sqlCmd)
$daValues.Fill($dsValues)
Write-Host $dsValues.Tables[0]
}
Error:
[ScriptBlock] $scriptBlock = $(throw <<<< "The parameter -scriptBlock is required.")
Edit [Graimer]
When I have moved the curly braces like below
cUsing($sqlConnection = New-Object System.Data.SqlClient.SqlConnection){
....
....
Write-Host $dsValues.Tables[0]}
I am not getting any error but the output which I am getting is
$sqlConnection.ConnectionString = "Server=myserver; Database=master; Integrated Security= True"
$sqlConnection.Open()
$sqlCmd = New-Object System.Data.SqlClient.SqlCommand
$sqlCmd.CommandText = "Select query"
$sqlCmd.Connection = $sqlConnection
$dsValues = New-Object System.Data.DataSet
$daValues = New-Object System.Data.SqlClient.SqlDataAdapter($sqlCmd)
$daValues.Fill($dsValues)
Write-Host $dsValues.Tables[0]
EDIT [Ansgar Wiechers]
when I have changed the scriptblock line to
Function cUsing {
param (
[System.IDisposable] $inputObject = $(throw "The parameter -inputObject is required."),
[ScriptBlock] $scriptBlock = ${throw "The parameter -scriptBlock is required."}
)
I am not getting any error but I am getting the same output which I have showed in Edit above.
The problem with your code is that you don't provide the scriptblock as a parameter, but as a command on it's own. Commands in Powershell are on a single line. Your code runs the command with only the using parameter, and fails. THEN you declare a scriptblock.
You have two options here; escape the linebreak(BAD practice), or move the opening curly brace one line up so it is considered part of the cusing command. Like this:
cUsing($sqlConnection = New-Object System.Data.SqlClient.SqlConnection) {
$sqlConnection.ConnectionString = "Server=myserver; Database=master; Integrated Security= True"
...
..
.
}
A script block is defined via curly braces, not via $(). Like this:
[ScriptBlock]$scriptBlock = { throw "The parameter ..." }
$() is the operator for evaluating subexpressions.
Add-Type #"
using System;
public class Code : System.IDisposable
{
public bool IsDisposed { get; set; }
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!IsDisposed)
{
IsDisposed = true;
}
}
}
"#
function using-block {
param (
[System.IDisposable]$InputObject = $(throw "The parameter -inputObject is required."),
[ScriptBlock]$ScriptBlock = $(throw "The parameter -scriptBlock is required.")
)
try { &$ScriptBlock }
finally {
if ($InputObject) {
if ($InputObject.PSBase) {
$InputObject.PSBase.Dispose()
} else {
$InputObject.Dispose()
}
}
}
}
using-block($c = New-Object Code) {
$c.IsDisposed
}
$c.IsDisposed
Outputs:
False
True
Related
So I writing some code to run some patching on AWS, I have the following script snippet taken out of the whole thing for now.. I seem to be running into an issue with $PSBoundParameters..
(It's Friday & I've had a long week so I may just need to sleep on it) - but I can't seem to pass anything out of read-host...
param (
[Parameter(Mandatory = $true)][ValidateNotNullorEmpty()][string]$Prof,
[Parameter(Mandatory = $false)][ValidateNotNullorEmpty()][string]$Reminder,
[Parameter(Mandatory = $false)][ValidateNotNullorEmpty()][string]$AddToTGs,
[Parameter(Mandatory = $false)][ValidateNotNullorEmpty()][string]$PatchType,
[Parameter(Mandatory = $false)][ValidateNotNullorEmpty()][string]$Instance,
[Parameter(Mandatory = $false)][ValidateNotNullorEmpty()][string]$Environment
)
function PromptInstance {
$Instance = Read-Host -Prompt "Please Specify the Instance"
Write-Host "Using: $Instance" -ForegroundColor Cyan
}
function PromptEnvtoPatch {
$Environment = Read-Host -Prompt "Please Specify the Environment (e.g. dev)"
Write-Host "Using: $Environment" -ForegroundColor Cyan
}
function PromptReminder {
$title = "Calendar"
$message = "Do you want to Add an Outlook Reminder in 24 Hrs?"
$pA = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Adds an Outlook Reminder"
$pB = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Won''t add a reminder"
$options = [System.Management.Automation.Host.ChoiceDescription[]]($pA, $pB)
$CalResult = $host.ui.PromptForChoice($title, $message, $options, 0)
switch ($CalResult)
{
0 {
$global:CalRes = 1
Write-Host "Reminder will be added.." -ForegroundColor Cyan
}
1 {
$global:CalRes = 0
Write-Host "No Reminder will be added.." -ForegroundColor Cyan
}
}
}
function PromptAddToTGs {
$title = "Re-Add to Target Groups"
$message = "Do you want to have this script Automatically add the instances back into the Target Groups after Patching?"
$pA = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Will ADD the instance back into Target Groups"
$pB = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Will NOT add the instance back into Target Groups"
$options = [System.Management.Automation.Host.ChoiceDescription[]]($pA, $pB)
$result = $host.ui.PromptForChoice($title, $message, $options, 1)
switch ($result)
{
0 {
$global:AddTGRes = 1
Write-Host "Instances WILL be added back into Target Groups" -ForegroundColor Cyan
}
1 {
$global:AddTGRes = 0
Write-Host "Instances will NOT be added back into Target Groups" -ForegroundColor Cyan
}
}
}
function PromptPatchType {
$title = "Patching Type"
$message = "Do you want to Patch a Single Instance, or ALL Instances for a specific Team Env?"
$pA = New-Object System.Management.Automation.Host.ChoiceDescription "&Instance", "Patches an Instance"
$pB = New-Object System.Management.Automation.Host.ChoiceDescription "&ALL Instances for an Env", "Patches ALL Instances in a Team Env"
$pQ = New-Object System.Management.Automation.Host.ChoiceDescription "&Quit", "Cancel/Exit"
$options = [System.Management.Automation.Host.ChoiceDescription[]]($pA, $pB, $pQ)
$PatchResult = $host.ui.PromptForChoice($title, $message, $options, 0)
switch ($PatchResult)
{
0 {
$Instance = Read-Host "Please Specify the Instance Id"
}
1 {
$Environment = Read-Host "Please Specify the Team (i.e. dev)"
}
2 {
Write-Host "You Quitter!... :-)"
Exit
}
}
}
function KickOffPatchingEnv {
param ($Prof, $Reg, $Reminder, $AddToTGs, $PatchType, $Environment)
Write-Host "Using the Following Options: (Profile:$Prof) (Region:$Reg) (Reminder:$Reminder) (AddToTGs:$AddToTGs) (PatchType:$PatchType) (Environment:$Environment)"
}
function KickOffPatchingInst {
param ($Prof, $Reg, $Reminder, $AddToTGs, $PatchType, $Instance)
Write-Host "Using the Following Options: (Profile:$Prof) (Region:$Reg) (Reminder:$Reminder) (AddToTGs:$AddToTGs) (PatchType:$PatchType) (Instance:$Instance)"
}
switch -wildcard ($Prof) {
"*dev*" { $Reg = "eu-west-1"; $Bucket = "s3-dev-bucket" }
"*admin*" { $Reg = "eu-west-1"; $Bucket = "s3-admin-bucket" }
"*prod*" { $Reg = "eu-west-1"; $Bucket = "s3-prod-bucket" }
"*staging*" { $Reg = "eu-west-1"; $Bucket = "s3-staging-bucket" }
}
if (!$PSBoundParameters['Reminder']) { PromptReminder }
if (!$PSBoundParameters['AddToTGs']) { PromptAddToTGs }
if ($PSBoundParameters['PatchType']) {
if (($PatchType -eq "i") -or ($PatchType -eq "instance") -or ($PatchType -eq "I") -or ($PatchType -eq "Instance")) {
if (!$PSBoundParameters['Instance']) { PromptInstance }
KickOffPatchingInst $Prof $Reg $Reminder $AddToTGs $PatchType $Instance
}
if (($PatchType -eq "a") -or ($PatchType -eq "all") -or ($PatchType -eq "A") -or ($PatchType -eq "All")) {
if (!$PSBoundParameters['Environment']) { PromptEnvtoPatch }
KickOffPatchingEnv $Prof $Reg $Reminder $AddToTGs $PatchType $Environment
}
} else { PromptPatchType }
If I use the parameters on the command line, it works fine..
PS C:\Users\myself\Desktop> .\test.ps1 -Prof dev -Reminder y -AddToTGs y -PatchType a -Environment dev
Using the Following Options: (Profile:dev) (Region:eu-west-1) (Reminder:y) (AddToTGs:y) (PatchType:a) (Environment:dev)
But if I omit an option, say for instance the Environment, I'm prompted for it, but the value is not displayed..
PS C:\Users\myself\Desktop> .\test.ps1 -Prof dev -Reminder y -AddToTGs y -PatchType a
Please Specify the Environment (e.g. dev): dev
Using: dev
Using the Following Options: (Profile:dev) (Region:eu-west-1) (Reminder:y) (AddToTGs:y) (PatchType:a) (Environment:)
Environment is empty....
I've tried loads of different things such as setting global:Environment etc, but I always seem to be missing out whichever variable isn't specified in the command?
Maybe this isn't the best way to write this, but i've never used $PSBoundParameters before so this is my first time trying it out..
Can anyone see my glaring error here at all?
TIA :)
#Mathias R. Jessen answered this for me.
I put this in the function
function PromptProfile {
$Prof = Read-Host -Prompt "Please Specify the Profile"
$global:UserProf = $Prof
}
then changed code later on with the global variable, so I can use them
When calling a unmanaged piece of code with pinvoke -- createprofile. The Powershell.exe process crashes after the call to the method in the unmanaged code. The profile is created successfully.
Why this would happen? My code is below:
function CreateProfile
{
param([String]$UserSid, [String]$UserName, [system.uint32]$ProfilePath)
Add-Type -TypeDefinition '
using System;
using System.Runtime.InteropServices;
public static class PInvoke {
[DllImport("userenv.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern int CreateProfile( [MarshalAs(UnmanagedType.LPWStr)] String pszUserSid, [MarshalAs(UnmanagedType.LPWStr)] String pszUserName, [Out][MarshalAs(UnmanagedType.LPWStr)] System.Text.StringBuilder pszProfilePath, uint cchProfilePath);
}
'
$pszProfilePath = new-object -typename System.Text.StringBuilder
[int]$results = [PInvoke]::CreateProfile($UserSid, $UserName, $pszProfilePath, $ProfilePath)
}
$stringbuff = new-object system.text.stringbuilder(260)
[system.uint32]$a =$stringbuff.capacity
$sid = ((get-aduser -id 'brtestlocaluser').sid.value)
CreateProfile -usersid $sid -username 'brtestlocaluser' -ProfilePath $a
Finally was able to figure this out. Calling it in another means seemed to fix this issue.
function Register-NativeMethod
{
[CmdletBinding()]
[Alias()]
[OutputType([int])]
Param
(
# Param1 help description
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true,
Position=0)]
[string]$dll,
# Param2 help description
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true,
Position=1)]
[string]
$methodSignature
)
$script:nativeMethods += [PSCustomObject]#{ Dll = $dll; Signature = $methodSignature; }
}
function Add-NativeMethods
{
[CmdletBinding()]
[Alias()]
[OutputType([int])]
Param($typeName = 'NativeMethods')
$nativeMethodsCode = $script:nativeMethods | ForEach-Object { "
[DllImport(`"$($_.Dll)`")]
public static extern $($_.Signature);
" }
Add-Type #"
using System;
using System.Text;
using System.Runtime.InteropServices;
public static class $typeName {
$nativeMethodsCode
}
"#
}
function New-ProfileFromSID {
[CmdletBinding()]
[Alias()]
[OutputType([int])]
Param
(
# Param1 help description
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true,
Position=0)]
[string]$UserName,
[string]$domain = ''
)
$methodname = 'UserEnvCP2'
$script:nativeMethods = #();
if (-not ([System.Management.Automation.PSTypeName]$methodname).Type)
{
Register-NativeMethod "userenv.dll" "int CreateProfile([MarshalAs(UnmanagedType.LPWStr)] string pszUserSid,`
[MarshalAs(UnmanagedType.LPWStr)] string pszUserName,`
[Out][MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszProfilePath, uint cchProfilePath)";
Add-NativeMethods -typeName $methodname;
}
$sb = new-object System.Text.StringBuilder(260);
$pathLen = $sb.Capacity;
Write-Verbose "Creating user profile for $Username";
#$SID= ((get-aduser -id $UserName -ErrorAction Stop).sid.value)
if($domain)
{
$objUser = New-Object System.Security.Principal.NTAccount($domain, $UserName)
$strSID = $objUser.Translate([System.Security.Principal.SecurityIdentifier])
$SID = $strSID.Value
}
else
{
$objUser = New-Object System.Security.Principal.NTAccount($UserName)
$strSID = $objUser.Translate([System.Security.Principal.SecurityIdentifier])
$SID = $strSID.Value
}
Write-Verbose "$UserName SID: $SID"
try
{
$result = [UserEnvCP2]::CreateProfile($SID, $Username, $sb, $pathLen)
if($result -eq '-2147024713')
{
$status = "$userName already exists"
write-verbose "$username Creation Result: $result"
}
elseif($result -eq '-2147024809')
{
$status = "$username Not Found"
write-verbose "$username creation result: $result"
}
elseif($result -eq 0)
{
$status = "$username Profile has been created"
write-verbose "$username Creation Result: $result"
}
else
{
$status = "$UserName unknown return result: $result"
}
}
catch
{
Write-Error $_.Exception.Message;
break;
}
$status
}
I have a script which look like below:
function Invoke-SQL {
param(
[string] $dataSource = ".\MSSQLSERVER",
[string] $database = "master",
[string] $sqlCommand = $(throw "Please specify a query.")
)
$connectionString = "Data Source=$dataSource; Integrated Security=True; Initial Catalog=$database; Connect Timeout=100"
$connection = new-object system.data.SqlClient.SQLConnection($connectionString)
$command = new-object system.data.sqlclient.sqlcommand($sqlCommand,$connection)
Try {
$connection.Open()
} catch {
return, $null
}
$adapter = New-Object System.Data.sqlclient.sqlDataAdapter $command
$dataset = New-Object System.Data.DataSet
$adapter.Fill($dataSet) | Out-Null
$connection.Close()
$dataSet.Tables
}
[string]$SQL = "select CmsSrvName from CmsServerList_VM"
$DbInstances = Invoke-SQL "DBSRV" "Test" $SQL
workflow wf1{
Param([System.Data.DataTable]$instance)
ForEach -Parallel -ThrottleLimit 10 ($i in $instance) {
InlineScript{
$t = $using:i
Write-Verbose "$t['CmsSrvName']"
}
}
}
wf1 -Verbose $DbInstances
Output:
VERBOSE: [localhost]:System.Data.DataRow['CmsSrvName']
VERBOSE: [localhost]:System.Data.DataRow['CmsSrvName']
VERBOSE: [localhost]:System.Data.DataRow['CmsSrvName']
The output is not what I expected, it just print out the type name not the value. How can I access the DataRow value in a workflow?(in Powershell 5)
Thanks in advance for the help
Please see my Function-to-be below:
Function Query {
param (
[string]$query
[string]$server
[string]$dbase
[string]$user
[string]$pass
)
if ($user) {
$connstr = "Server={0};Database={1};User ID={2};Password={3};Trusted_Connection=False;Connect Timeout=15" -f $server, $dbase, $user, $pass
}
else {
$connstr = "Server={0};Database={1};Integrated Security=True;Connect Timeout=15" -f $server, $dbase
}
$conn.ConnectionString = $connstr
switch ($query.Split()[0]) {
"SELECT" {
$cmd = New-Object System.Data.SqlClient.SqlCommand($query,$conn)
$adapter = New-Object System.Data.SqlClient.SqlDataAdapter($cmd)
$dataset = New-Object System.Data.DataSet
$adapter.Fill($dataset) | Out-Null
return $dataset
}
"UPDATE" {
$cmd = New-Object System.Data.SqlClient.SqlCommand($query,$conn)
return $cmd.ExecuteNonQuery()
}
"INSERT" {
$cmd = New-Object System.Data.SqlClient.SqlCommand($query,$conn)
return $cmd.ExecuteNonQuery()
}
}
}
Query -query "SELECT TOP 10 myField FROM myTable" -server "SQLEXPRESS" -dbase "TEST"
This doesn't work, Powershell ISE gives me red in the param section but I don't understand why. Because of the many different ways people seem to construct Powershell functions (I'm a beginner :)) I am somewhat confused.
How do I make this function work?
Put commas , between the parameters:
Function SomeName {
Param ($param1,$param2,$param3)
}
etc. You can use whitespace or line break after the comma for readability, which is what most people do:
Function SomeName {
param (
$param1,
$param2
)
}
I'm attempting to create a script cmdlet with dynamic parameters for each of the targets in an MSBuild project file. It's working with one minor annoyance - it only autocompletes the static parameters until I type the first character(s) of a target parameter.
My script with the cmdlet "Make-Project" follows.
What would cause DynamicParam to not return parameters unless the first part of the parameter is entered?
Set-Alias mk Make-Project
function Make-Project
{
[CmdletBinding(DefaultParameterSetName="Build")]
PARAM(
[Parameter(ParameterSetName="Build")]
[Switch]$Build,
[Parameter(ParameterSetName="Clean")]
[Switch]$Clean,
[Parameter(ParameterSetName="Rebuild")]
[Switch]$Rebuild,
[ValidateSet( "q","quiet", "m","minimal", "n","normal", "d","detailed", "diag","diagnostic" )]
[string]$BuildVerbosity,
[Switch]$CertificationBuild,
[Switch]$BuildDebug
)
PROCESS
{
# Defaults
$certBuild = ""
$target = "Usage"
$buildTarget = "Build"
$verbosity = "minimal"
$configuration = "release"
if ( [System.Convert]::ToBoolean( $env:project_build_debug ) )
{ $configuration = "debug" }
foreach( $paramName in $MyInvocation.BoundParameters.Keys )
{
switch -RegEx ( $paramName )
{
"(?i)CertificationBuild" { $certBuild = "cert" }
"(?i)^(Build|Clean|Rebuild)$" { $buildTarget = $paramName }
"(?i)^BuildVerbosity$" { $verbosity = $MyInvocation.BoundParameters[ $paramName ] }
"(?i)^BuildDebug$" { $configuration = "debug" }
default { $target = $paramName }
}
}
$msbuildexe = Get-MSBuildExe
if ( $msbuildexe.Contains( "v4.0" ) )
{ $cmd = "$msbuildexe /v:$verbosity $certBuild /m /property:Configuration=$configuration /property:BuildTarget=$buildTarget /target:$target /property:CLR4=1 Project.proj" }
else
{ $cmd = "$msbuildexe /v:$verbosity $certBuild /m /property:Configuration=$configuration /property:BuildTarget=$buildTarget /target:$target /tv:3.5 Project.proj" }
Write-Host $cmd
Invoke-Expression $cmd
}
DynamicParam
{
$projFile = '.\Project.Proj'
$projXml = [xml]( Get-Content $projFile )
$paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
$projXml.Project.Target | % {
$paramName = $_.Name
$attribute = New-Object System.Management.Automation.ParameterAttribute
$attribute.ParameterSetName = "__AllParameterSets"
$attribute.Mandatory = $false
$attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
$attributeCollection.Add($attribute)
$param = New-Object System.Management.Automation.RuntimeDefinedParameter( $paramName, [Switch], $attributeCollection )
$paramDictionary.Add( $paramName, $param )
}
return $paramDictionary
}
}
function Get-MSBuildExe
{
$bitness = ""
if ( $env:PROCESSOR_ARCHITECTURE -eq "AMD64" )
{ $bitness = "64" }
$msbuildexe = "$env:SystemRoot\Microsoft.NET\Framework${bitness}\v4.0.30319\MSBuild.exe"
if ( -not (Test-Path $msbuildexe) )
{ $msbuildexe = "$env:SystemRoot\Microsoft.NET\Framework${bitness}\v3.5\MSBuild.exe" }
$msbuildexe
}
I would suggest that you not add a bunch of switch parameters. Instead, add one dynamic parameter named "Target" and add a ValidateSet attribute to it with the list of targets.
This is a helpful article.
http://robertrobelo.wordpress.com/2010/02/12/add-parameter-validation-attributes-to-dynamic-parameters/