Check if XML Node is true, and if so, execute a SQL statement - powershell

I'm working on a script that reads from an XML list of environments and reiterates through each entry, restores a database, runs schema changes on the database, and then backs up said database. I have all of these pieces working, but I'd like to add logic to the script to include a step to set the DB Owner before running the schema changes if the XML node exists;
Currently, if I run this, it writes 'No DBOwner Specified' no matter what.
$dbOwnerExists = $($Environment.Backup.DatabaseBackup.DBOwner)
if ($dbOwnerExists -eq $true) {
Invoke-Sqlcmd -ServerInstance "$($DatabaseBackup.Instance)" -Query "EXEC sp_changedbowner $($Environment.Backup.DatabaseBackup.DBOwner)"
} else {
Write-Output 'No DBOwner Specified'
}
Current XML Looks like this
<Environments>
<Environment Database="exampleDatabase">
<Backup Restore="TRUE">
<DatabaseBackups Instance="SQLInstance" SrcLocation="\\Hostname\c$\temp\example.bak" DstLocation="\\Hostname\c$\temp\example.bak" DBOwner=""sa""/>
<DatabaseBackups Instance="SQLInstance" SrcLocation="\\Hostname\c$\temp\example2.bak" DstLocation="\\Hostname\c$\temp\example2.bak"/>
</Backup>
</Environment>
</Environments>
It seems that powershell doesn't like my logic to check if DBOwner exists in the XML, maybe $true is not the best way to check this?

I haven't seen your full code so this is the best I can do.
Let's assume that you have the XML stored in variable $XML and $XML was defined like this:
[xml]$xml = #"
<Environments>
<Environment Database="exampleDatabase">
<Backup Restore="TRUE">
<DatabaseBackups Instance="SQLInstance" SrcLocation="\\Hostname\c$\temp\example.bak" DstLocation="\\Hostname\c$\temp\example.bak" DBOwner=""sa""/>
<DatabaseBackups Instance="SQLInstance" SrcLocation="\\Hostname\c$\temp\example2.bak" DstLocation="\\Hostname\c$\temp\example2.bak"/>
</Backup>
</Environment>
</Environments>
"#
You can check whether DBOwner exists or not just by checking the value with
$XML.Environments.Environment.Backup.DatabaseBackups.DBOwner
Which with the XML would output:
"sa"
But to get a true/false value on whether it exists or not, you can use the [bool] datatype like you did in your answer:
[bool]$XML.Environments.Environment.Backup.DatabaseBackups.DBOwner
But I found a glitch with this in which it would still output true if the node didn't exist like so:
[bool]$XML.Environments.Environment.Backup.DatabaseBackups.fasdasd
True
so I changed it to this.
[bool][string]$($XML.Environments.Environment.Backup.DatabaseBackups.DBOwner).trim
Which in this case would output:
True
And to put this in an if statement since it is already a boolean you could put it in the in statement like so:
if([bool][string]$($XML.Environments.Environment.Backup.DatabaseBackups.DBOwner).trim){
Invoke-Sqlcmd -ServerInstance "$($DatabaseBackup.Instance)" -Query "EXEC sp_changedbowner $($Environment.Backup.DatabaseBackup.DBOwner)"
} else {
Write-Output 'No DBOwner Specified'
}
To test if this works we can also try doing this on something that doesn't exist like $XML.Environments.Environment.Backup.DatabaseBackups.SomethingThatDoesntExist which should return false and does like so:
[bool][string]($XML.Environments.Environment.Backup.DatabaseBackups.SomethingThatDoesntExist).trim()
False
To test the if statement we can do
if([bool][string]$($XML.Environments.Environment.Backup.DatabaseBackups.DBOwner).trim){
Write-Output "Exists"
} else {
Write-Warning 'No DBOwner Specified'
}
Which would output
Exists
and we could also test if it doesn't exist with
if([bool][string]$($XML.Environments.Environment.Backup.DatabaseBackups.fasdasd).trim){
Write-Output "Exists"
} else {
Write-Warning 'No DBOwner Specified'
}
The output would be:
WARNING: No DBOwner Specified
NOTE: The [bool]s in the if statements are unnecessary since if already converts things to booleans but I just put it in for easier understanding this would work fine too:
if([string]$($XML.Environments.Environment.Backup.DatabaseBackups.fasdasd).trim){
Write-Output "Exists"
} else {
Write-Warning 'No DBOwner Specified'
}

I was able to figure this out with the following, which works well enough for me!
$dbOwnerExists=$null
$dbOwnerExists = $($DatabaseBackup.DBOwner)
if ($dbOwnerExists) {
Invoke-Sqlcmd -ServerInstance "$($DatabaseBackup.Instance)" -Query "USE $($Environment.Database) EXEC sp_changedbowner '$($DatabaseBackup.DBOwner)'"
} else {
Write-Output 'No DBOwner Specified'
}

Related

Not able to run an exe in jenkins pipeline using powershell

I am trying to execute a process which is written in c# through jenkins pipeline during the build and deployment process.
It is a simple executable which takes 3 arguments, when it gets called from jenkins pipeline using a powershell function it doesn't write any logs which are plenty within the code of this exe, also it does not show anything on the pipeline logs as to what happened to this process. Whereas the logs output is clean before and after the execution of this process i.e. "Started..." & "end" gets printed in the jenkins build log.
When i try to run the same exe on a server directly with the same powershel script it runs perfectly fine. Could you please let me know how can i determine whats going wrong here or how can i make the logs more verbose so i can figure out the root cause.
Here is the code snippet
build-utils.ps1
function disable-utility($workspace) {
#the code here fetches the executable and its supporting libraries from the artifactory location and unzip it on the build agent server.
#below is the call to the executable
Type xmlPath #this prints the whole contents of the xml file which is being used as an input to my exe.
echo "disable exe path exists : $(Test-Path ""C:\Jenkins\workspace\utils\disable.exe"")" // output is TRUE
echo "Started..."
Start-Process -NoNewWindow -Filepath "C:\Jenkins\workspace\utils\disable.exe" -ArgumentList "-f xmlPath 0" #xmlPath is a path to a xml file
echo "end."
}
jenkinsfile
library {
identifier: 'jenkins-library#0.2.14',
retriever: legacySCM{[
$class: 'GitSCM',
userRemoteConfigs: [[
credtialsId: 'BITBUCKET_RW'
url: <htps://gitRepoUrl>
]]
]}
}
def executeStep(String stepName) {
def butil = '.\\build\\build-utils.ps1'
if(fileExists(butil))
{
def status = powershell(returnStatus: true, script: "& { . '${butil}'; ${stepName}; }")
echo status
if(status != 0) {
currentBuild.Result = 'Failure'
error("$StepName failed")
}
}
else
{
error("failed to find the file")
}
}
pipeline {
agent {
docker {
image '<path to the docker image to pull a server with VS2017 build tools>'
lable '<image name>'
reuseNode true
}
}
environment {
#loading the env variables here
}
stages {
stage {
step {
executeStep("disable-utility ${env.workspace}")
}
}
}
}
Thanks a lot in advance !
Have you changed it ? go to Regedit [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System Set "EnableLUA"= 0

PowerShell unable to find type of exception in try/catch block

I'm running a PowerShell Function App (~3) which uses the Az PowerShell module to manage a Storage Account. If any of the operations I carry out result in an error, I am unable to check for specific types in a try/catch block.
It is important to note that where there is no error, operations using the Az.Storage module are successful.
For example, if I were to try and delete a container that does not exist, the example below results in the following error -
Unable to find type [Microsoft.WindowsAzure.Commands.Storage.Common.ResourceNotFoundException].
To obtain the type of exception that is returned, I'm using $_.Exception.GetType().fullname.
I've also tried to add the namespace to the script that may produce the exceptions.
using namespace Microsoft.WindowsAzure.Commands.Storage.Common
Example
Class Storage
{
[AppSettings]$AppSettings = [AppSettings]::GetInstance()
[Object]$Context
Storage()
{
$key = Get-AzStorageAccountKey -ResourceGroupName $this.AppSettings.StorageAccountResourceGroup -Name $this.AppSettings.StorageAccountName
$this.Context = New-AzStorageContext -StorageAccountName $this.AppSettings.StorageAccountName -StorageAccountKey $key[0].Value
}
[void] DeleteBlobContainer([String]$name)
{
try {
Remove-AzStorageContainer -Name $name -Context $this.Context -Force -ErrorAction Stop
}
catch [Microsoft.WindowsAzure.Commands.Storage.Common.ResourceNotFoundException] {
throw [ContainerNotFoundException]::new($name)
}
catch {
throw [DustBinException]::new($_.Exception.Message)
}
}
}
Update
When calling an HTTP triggered function, I am able to see that the Az.Storage module is installed. This is expected, given operations that require the module are successful -
Get-Module -Name Az.Storage -ListAvailable | Select-Object Name, Version, ModuleBase | ConvertTo-Json
[
{
"Name": "Az.Storage",
"Version": {
"Major": 3,
"Minor": 0,
"Build": 0,
"Revision": -1,
"MajorRevision": -1,
"MinorRevision": -1
},
"ModuleBase": "C:\\Users\\dgard\\AppData\\Local\\AzureFunctions\\DustBin\\ManagedDependencies\\201202095548376.r\\Az.Storage\\3.0.0"
}
]
However, if copy the module to .\bin and include a Module manifest to require Microsoft.Azure.Storage.Common.dll, as suggested in this question, the type is still not found.
New-ModuleManifest ./Modules/StorageHelper/StorageHelper.psd1 -RootModule StorageHelper.psm1 -RequiredAssemblies .\bin\Az.Storage\3.0.0\Microsoft.Azure.Storage.Common.dll
To be sure I was adding the correct assembly, I have updated the manifest to include every single assembly in the Az.Storage module, but the type is still not found.
In my question I added an update mentioning that I had tried to add a module manifest requiring all of the Az.Storage assemblies; this was not quite correct...
I had copied the list of required assemblies from the module manifest included with the Az.Storage module, but this did not include Microsoft.Azure.PowerShell.Cmdlets.Storage.dll. Using a module manifest to require this assembly (just this one, no others required) has worked.
New-ModuleManifest ./Modules/StorageHelper/StorageHelper.psd1 -RootModule StorageHelper.psm1 -RequiredAssemblies .\bin\Az.Storage\3.0.0\Microsoft.Azure.PowerShell.Cmdlets.Storage.dll

How do I perform a logical If..then in powershell against JSON data?

I'm trying to loop through a JSON array of desired registry values, and then inspect the registry value for the correct setting.
The issue I have is that I'm not correctly defining the 'if..()' logic test in my loop. The problem code is located in the line: if($protocols[$i][$tempdisabledKVString] -eq "true")
I have the following object:
$protocolsJSON = #"
[
{
"Name": "TLS 1.2",
"Server-Enabled": True,
"Client-Enabled": True
}
]
"#
$protocols = $protocolsJSON | ConvertFrom-Json
Which fails the nested if statement below (undesired behavior)
elseif ($isDefaultDisabled -eq 0) # Protocol is manually enabled in registry (part 1.A)
{
if($protocols[$i][$tempdisabledKVString] -eq "True") # Protocol should be manually enabled in registry (part 1.B)
{
# For TLS 1.2 to be enabled and negotiated on servers that run Windows Server 2008 R2,
# you MUST create the DisabledByDefault entry in the appropriate subkey (Client, Server)
# and set it to "0". The entry will not be seen in the registry and it is set to "1" by default.
$errorString = "Warning: Protocol is only partially enabled."
$TLSProtocolResult.Errors = $errorString
}
else
{
write-host "DEBUG " $protocols[$i][$tempdisabledKVString]
write-host "DEBUG " $protocols[$i]
write-host "DEBUG " [$tempdisabledKVString]
$errorString = "Error: Protocol should be disabled."
$TLSProtocolResult.Errors = $errorString
}
}
Which produces the following output
DEBUG
DEBUG #{Name=TLS 1.2; Server-Enabled=True; Client-Enabled=True}
DEBUG [Server-Disabled]
DEBUG
DEBUG #{Name=TLS 1.2; Server-Enabled=True; Client-Enabled=True}
DEBUG [Client-Disabled]
How do I edit the IF statement so that I can test the true/false status of $protocols[$i][$tempdisabledKVString]?
The problem is you're trying to access a property as if it were a nested array.
Try this:
$protocolsJSON = #"
[
{
"Name": "TLS 1.2",
"Server-Enabled": true,
"Client-Enabled": true
}
]
"#
$protocols = $protocolsJSON | ConvertFrom-Json
$property = "Server-Enabled"
Write-Host "RESULT: $($protocols[0].$property)"
Your issue is most likely the JSON not being parsed. Try including quotes around your values. i.e. replace: "Server-Enabled": True, with "Server-Enabled": "True",.
Also, when if $tempdisabledKVString is the name of a property, you need to access it as a property rather than an index. i.e. replace $protocols[$i][$tempdisabledKVString] with $protocols[$i]."$tempdisabledKVString".
Clear-Host
$protocolsJSON = #"
[
{
"Name": "TLS 1.2",
"Server-Enabled": "True",
"Client-Enabled": "True"
}
]
"#
$protocols = $protocolsJSON | ConvertFrom-Json
$i = 0
$tempdisabledKVString = 'Server-Enabled'
if ($protocols[$i]."$tempdisabledKVString" -eq 'True') {
":)"
} else {
":("
}
In theory these issues should have caused exceptions to be thrown. For some reason you're not seeing those, or that would have prompted you to find the cause. Please check the value of $ErrorActionPreference; by default it should be set to Continue; but it looks like it may have been updated to SilentlyContinue for your session. It's OK to have this setting in some scenarios; but generally better to have errors be thrown when they occur so that you can see what's going wrong.

MongoDB migrations for .NET Core

I have a .NET Core project that works with mongoDb.
I did the research about data migrations and neither one solution works for me.
There is MongoMigrations NuGet package but it is not compatible with .NET Core.
A goal is to have some method that will check current db version and if it is not up to date, to do the certain update, for example to call some RemoveUserCollection() method or something like that.
Did anyone have the same problem, or even better a solution for that? :)
Thanks!
I've stumbled on this issue a few times myself but it takes pretty much 2mins to just manually bake it into your application startup or just script it out using javascript and deploy it like any other system change.
I've got a PowerShell script like
param([Parameter(Mandatory=$True)][string]$Hostname, [string]$Username = $null, [string]$Password = $null)
if($Username){
$authParameters = "-u $Username -p $Password --authenticationDatabase admin"
}
Write-Host "Running all migration scripts..."
$scripts = Get-Childitem | where {$_.extension -like '.js'} | foreach { $_.Name }
Invoke-Expression "c:\mongodb\bin\mongo.exe --host $Hostname $authParameters $scripts"
then I just dump a load of .js files in the same directory.
;
(function () {
var migration = { _id: ObjectId("568b9e75e1e6530a2f4d8884"), name: "Somekinda Migration (929394)" };
var testDb = db.getMongo().getDB('test');
if (!testDb.migrations.count({ _id: migration._id })) {
testDb.migrations.insert(migration);
print('running ' + migration.name)
// Do Work
testDb.people.find().snapshot().forEach(
function (alm) {
testDb.people.save(
{
modifiedAt: new Date(),
});
}
)
}
})();

Assertion over each item in collection in Pester

I am doing some infrastructure testing in Pester and there is repeating scenario that I don't know how to approach.
Let's say, I want to check whether all required web roles are enabled on IIS. I have a collection of required web roles and for each of them I want to assert it is enabled.
My current code looks like this:
$requiredRoles = #(
"Web-Default-Doc",
"Web-Dir-Browsing",
"Web-Http-Errors",
"Web-Static-Content",
"Web-Http-Redirect"
)
Context "WebRoles" {
It "Has installed proper web roles" {
$requiredRoles | % {
$feature = Get-WindowsOptionalFeature -FeatureName $_ -online
$feature.State | Should Be "Enabled"
}
}
}
It works in the sense that the test will fail if any of the roles are not enabled/installed. But that is hardly useful if the output of such Pester test looks like this:
Context WebRoles
[-] Has installed proper web roles 2.69s
Expected: {Enabled}
But was: {Disabled}
283: $feature.State | Should Be "Enabled"
This result doesn't give any clue about which feature is the Disabled one.
Is there any recommended practice in these scenarios? I was thinking about some string manipulation...
Context "WebRoles" {
It "Has installed proper web roles" {
$requiredRoles | % {
$feature = Get-WindowsOptionalFeature -FeatureName $_ -online
$toCompare = "{0}_{1}" -f $feature.FeatureName,$feature.State
$toCompare | Should Be ("{0}_{1}" -f $_,"Enabled")
}
}
}
which would output:
Context WebRoles
[-] Has installed proper web roles 2.39s
Expected string length 27 but was 28. Strings differ at index 20.
Expected: {IIS-DefaultDocument_Enabled}
But was: {IIS-DefaultDocument_Disabled}
-------------------------------^
284: $toCompare | Should Be ("{0}_{1}" -f $_,"Enabled")
...which is better, but it doesn't feel very good...
Also, there is second problem with the fact that the test will stop on first fail and I would need to re-run the test after I fix each feature...
Any ideas?
Put your It inside the loop like so:
Context "WebRoles" {
$requiredRole | ForEach-Object {
It "Has installed web role $_" {
(Get-WindowsOptionalFeature -FeatureName $_ -online).State | Should Be "Enabled"
}
}
}