Pass parameters to script using Start-DSCConfiguration - powershell

I have a DSC configuration that looks like this
configuration DBServer
{
param(
[PSCredential]$SqlAdminCredential
)
node "localhost"
{
Script ConfigureSSRS {
SetScript = {
& sqlcmd -S $RSConnection -i $DBCreateFile -U $SQLAdminCredential.UserName -P $SQLAdminCredential.GetNetworkCredential().Password
I'm running it on azure VMs using a JSON template deployment with a DSC extension containing this Properties node:
"Properties": {
"SqlAdminCredential": {
"userName": "PrivateSettingsRef:SQLAdmin",
"password": "PrivateSettingsRef:SQLAdminPass"
},
Those two value are indeed defined in the protectedSettings node:
"SQLAdmin": "[parameters('sqlAuthenticationLogin')]",
"SQLAdminPass": "[parameters('sqlAuthenticationPassword')]"
But I keep getting null reference exceptions in the code I listed above whenever I try to use the credentials. I've tried $using:SqlAdminCredential and I believe yesterday I tried $global:SqlAdminCredential as well.
I'm currently attempting to troubleshoot this but I don't like making a change to my configuration and then spending 15 minutes waiting for a deploy to see if it worked. However, I can't figure out how to pass this credential object parameter using Start-DscConfiguration in order to troubleshoot it on an existing VM. How can I do this?
Bonus points if you can fix my actual problem and tell me how to access $SqlAdimCredential from inside ConfigureSSRS.SetScript

For the moment, I have decided to be poorly behaved and do this:
configuration DBServer
{
param(
[PSCredential]$SqlAdminCredential
)
node "localhost"
{
$adminCreds = $SqlAdminCredential #a hack
Script ConfigureSSRS {
SetScript = {
$SqlAdminCredential = $using:adminCreds
I will wait for a better solution but this appears to be working.

You have the following options:
Use the PSDSCRunAsCredential of the Script Resource if you need the entire script to run under a single user.
Create your own DSC Resource that uses PSCredential Parameters.

Related

Is there a way to mock an $? return using Pester?

I'm writing tests for a PowerShell application, using Pester.
I have been able to create mocks for most functions, but I haven't been able to mock a function returning the $? variable. I'm currently using it to evaluate the returns from AWS CLI commands.
This is, for example, to mock a failing AWS CLI command return.
Any thoughts?
If you're looking to get mock code which performs the same function as $?, you can use something like this (it is quite limited in terms of how it actually returns if multiple lines are executed at once, etc., and it will likely need to be modified depending on the execution context):
Function Test-LastCommandError {
$LastCommand = (History | Select -Last 1).CommandLine
$LastError = $Error[-1].InvocationInfo.Line
$LastCommand -eq $LastError
}
1/1#Success
Test-LastCommandError # Returns false
1/0#Error
Test-LastCommandError # Returns true
This works for me manually executing each line, but not in an ad-hoc ISE window (since it copy-pastes everything as one command on execute).
We created an auxiliary function and passed the $? value.
function Test-LastExitCodeIsFalse ($last_exit) {
if ($last_exit) {
return $false
}
$true
}
And a usage being
aws s3 <an aws command>
if (Test-LastExitCodeIsFalse($?)) {
throw "AWS Exception"
}
Using Pester, then we just mock the Test-LastExitCodeIsFalse function to return false. And we have a unit test with a failing AWS instance :)
You should be able to do:
Mock AWC-CLI { $ = $true } -Verifiable

Modify datasource IP addresses in WebSphere Application Server

I have nearly a hundred data sources in a WebSphere Application Server (WAS) and due to office relocation, the IP of the database servers have changed and I need to update the datasource IP addresses in my WAS too.
Considering it error-prone to update hundred IPs through admin console.
Is there any way that I can make the change by updating config files or running a script? My version of WAS is 7.0.
You should be able to use the WAS Admin Console's built-in "command assistance" to capture simple code snippets for listing datasources and changing them by just completing those operations in the UI once.
Take those snippets and create a new jython script to list and update all of them.
More info on command assistance:
http://www.ibm.com/developerworks/websphere/library/techarticles/0812_rhodes/0812_rhodes.html
wsadmin scripting library:
https://github.com/wsadminlib/wsadminlib
You can achieve this using wsadmin scripting. Covener has the right idea with using the admin console's command assistance to do the update once manually (to get the code) and then dump that into a script that you can automate.
The basic idea is that a datasource has a set of properties nested under it, one of which is the ip address. So you want to write a script that will query for the datasource, find its nested property set, and iterate over the property set looking for the 'ipAddress' property to update.
Here is a function that will update the value of the "ipAddress" property.
import sys
def updateDataSourceIP(dsName, newIP):
ds = AdminConfig.getid('/Server:myServer/JDBCProvider:myProvider/DataSource:' + dsName + '/')
propertySet = AdminConfig.showAttribute(ds, 'propertySet')
propertyList = AdminConfig.list('J2EEResourceProperty', propertySet).splitlines()
for prop in propertyList:
print AdminConfig.showAttribute(prop, 'name')
if (AdminConfig.showAttribute(prop, 'name') == 'ipAddress'):
AdminConfig.modify(prop, '[[value '" + newIP + "']]')
AdminConfig.save();
# Call the function using command line args
updateDataSourceIP(sys.argv[0], sys.argv[1])
To run this script you would invoke the following from the command line:
$WAS_HOME/bin/wsadmin.sh -lang jython -f /path/to/script.py myDataSource 127.0.0.1
**Disclaimer: untested script. I don't know the name of the "ipAddress" property off the top of my head, but if you run this once it will print out all the properties on your ds, so you can get it there
Some useful links:
Basics about jython scripting
Modifying config objects using wsadmin
As an improvement to aguibert's script, to avoid having to provide all 100 datasource names and to update it to correct the containment path of the configuration id, consider this script which will update all datasources, regardless of the scope at which they're defined. As always, backup your configuration before beginning and once you're satisified the script is working as expected, replace the AdminConfig.reset() with save(). Note, these scripts will likely not work properly if you're using connection URLs in your configuration.
import sys
def updateDataSourceIP(newIP):
datasources = AdminConfig.getid('/DataSource:/').splitlines()
for datasource in datasources:
propertySet = AdminConfig.showAttribute(datasource, 'propertySet')
propertyList = AdminConfig.list('J2EEResourceProperty', propertySet).splitlines()
for prop in propertyList:
if (AdminConfig.showAttribute(prop, 'name') == 'serverName'):
oldip = AdminConfig.showAttribute(prop, 'value')
print "Updating serverName attribute of datasource '" + datasource + "' from " + oldip + " to " + sys.argv[0]
AdminConfig.modify(prop, '[[value ' + newIP + ']]')
AdminConfig.reset();
# Call the function using command line arg
updateDataSourceIP(sys.argv[0])
The script should be invoked similarly to the above, but without the datasource parameter, the only parameter is the new hostname or ip address:
$WAS_HOME/bin/wsadmin.sh -lang jython -f /path/to/script.py 127.0.0.1

Starting Runbook via Azure API, Portal, or ISE Add-On yields "input parameter type mismatch" error

Given a simple runbook:
workflow test
{
[CmdletBinding()]
param([string] $NumericString)
write-output $NumericString
}
When starting it with a numeric value (ie: 5) via the Azure Portal as a new Job (published), via the Test Pane, or using the Azure Automation PowerShell ISE Add-On, the following error is returned and the execution Fails.
[edit] Just out of curiosity I tried some other values. 'true' or 'false' (without quotes in ise/the ui) will also cause the error (and are sent to the API inside quotes).[\edit]
The values provided for the root activity's arguments did not satisfy
the root activity's requirements: 'DynamicActivity': Expected an input
parameter value of type 'System.String' for parameter named 'Numeric'.
Parameter name: rootArgumentValues
AFAIK, this is not a factor when I've executed via a parent runbook, webhook, etc.
The PowerShell ISE Add-On issues this PUT request to the API:
(https://management.azure.com/subscriptions/<id>/resourceGroups/<rg>/providers/Microsoft.Automation/automationAccounts/<aa>/runbooks/<rb>/draft/testJob?api-version=2015-10-31)
{
"parameters": {
"Numeric": "5" <-- notice it's a string
}
}
This is a bug in the Automation portal and ISE add on. For portal, this should be fixed in a week or so. For ISE add on, can you please file a bug on this here: https://github.com/azureautomation/azure-automation-ise-addon/issues
This would appear to be a bug, similar to Azure Automation Error 'DynamicActivity': Expected an input parameter value of type
To mitigate the issue, just wrap your numeric value in quotes in the Portal or ISE Add-On
If you are starting runbooks outside of the formal SDKs, it appears you need to know about required extra escaping for sending Numeric or Boolean values for string parameters (in the least).
This is what the subsequent PUT request should look like (from ISE Add-On)
{
"parameters": {
"Numeric": "\"5\""
}
}

Powershell remoting and page file

I wrote a powershell script that connects to a remote machine with the intent of executing a software rollout on said machine. Basically it connects, maps a drive, copies the rollout from the mapped drive to the target machine, then executes a perl script to install the rollout. If I do those steps manually everything works fine. When I try using my script, the perl script fails on the remote machine saying, "The paging file is too small for this operation to complete".
Can someone explain the considerations I need to take into account when operating remotely? I've tried monitoring memory usage and I don't see anything out of the ordinary. Is the page file OS wide or is there some type of per user configuration my script should be setting when it connects?
I can post snippets of my script if needed, but the script is 426 lines so I think it would be overwhelming to post in its entirety.
I found that the remote shells are managed differently than logging onto the box and executing a powershell session. I had to increase the maximum amount of memory available using one of the commands below:
Set-Item WSMan:\localhost\Shell\MaxMemoryPerShellMB 1024
winrm set winrm/config #{MaxMemoryPerShellMB="1024"}
The default is 150MB which didn't cut it in my case. I can't say that I recommend 1GB, I'm just a developer. I tried upping it until I found what worked for me.
I tried this code to run the puppet client as an administrator but the framework still complains with "Access Denied"
Exe (C:\Users\lmo0\AppData\Local\Temp\Microsoft .NET Framework 4 Setup_4.0.30319\Windows6.1-KB958488-v6001-x64.msu) failed with 0x5 - Access is denied. .
using System;
using System.Diagnostics;
namespace RunAsAdmin
{
class Program
{
static void Main(string[] args)
{
Process proc = new Process();
Process p = new Process();
p.StartInfo.FileName = #"powershell.exe";
p.StartInfo.Arguments = #"invoke-command -computername vavt-pmo-sbx24 -ScriptBlock {&'C:\Program Files (x86)\Puppet Labs\Puppet\bin\puppet.bat' agent --test --no-daemonize --verbose --logdest console}";
p.StartInfo.Verb = "runas";
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
p.Start();
while (p.HasExited == false) {
Console.WriteLine(p.StandardOutput.ReadLine());
}
Console.ReadLine();
p.WaitForExit();
p.Close();
}
}
}

Use AppCmd to LIST CONFIG in APPHOST only

I have a requirement to use powershell to configure IIS7.5 on WebApplications that have not yet had code deployed (possibly at all, possibly old/broken web.configs exist) to the file system. I would like to be able to do this all at the APPHOST level. (Note at the bottom about use of Powershell > AppCmd).
I can SET all the values properly, however, being somewhat diligent, I like to also validate the values were set properly by retrieving them after setting.
Here's the scenario:
I can set this value using AppCmd so the setting is applied at the APPHOST level using the /Commit:APPHOST flag. However, I havent found a way to READ the values exclusively at the APPHOST level.
Setting the Code is successful:
C:\Windows\System32\inetsrv\appcmd.exe set config "webSiteName/webAppName" -section:system.webServer/security/authentication/anonymousAuthentication /enabled:"True" /commit:apphost
However, I cant find a way to read the values using AppCmd (or Powershell):
Running the following AppCmd returns an error due to the broken pre-existing web.config in the folder (the specific error is unimportant, as it is reading the WebApp's web.config instead of the ApplicationHost.config/APPHOST):
C:\Windows\System32\inetsrv\appcmd.exe list config "MACHINE/WEBROOT/APPHOST/webSiteName/webAppName" -section:system.webServer/security/authentication/anonymousAuthentication
ERROR ( message:Configuration error
Filename: \\?\c:\inetpub\wwwroot\webSiteName\webAppName\web.config
Line Number: 254
Description: The configuration section 'system.runtime.caching' cannot be read because it is missing a section declaration
. )
Note: I would prefer to do this all in Powershell instead of using AppCmd, so if anyone has the syntax for modifying the APPHOST settings for anonymousAuthentication section of a WebApplication, that lives under a Website, from inside Powershell (Get-WebConfiguration seems to only use the WebApp web.config), that would be totally awesome and much appreciated!
Here's how to do this in PowerShell:
[Reflection.Assembly]::Load(
"Microsoft.Web.Administration, Version=7.0.0.0,
Culture=Neutral, PublicKeyToken=31bf3856ad364e35") > $null
$serverManager = New-Object Microsoft.Web.Administration.ServerManager
$config = $serverManager.GetApplicationHostConfiguration()
$anonymousAuthenticationSection = $config.GetSection("system.webServer/security/authentication/anonymousAuthentication", "simpleasp.net")
Write-Host "Current value: " $anonymousAuthenticationSection["enabled"]
# Now set new value
$anonymousAuthenticationSection["enabled"] = $true
$serverManager.CommitChanges()