SQLPackage.exe filename with the date - cmd

I am trying to automate a backup of an Azure database to my local machine using SQLPackage.exe. I am trying to add the date onto the filename so that every night it doesn't get overwritten.
The following line will pick up the date but will then stop the backup running with the error shown below
CMD
"C:\Program Files (x86)\Microsoft SQL Server\110\DAC\bin\sqlpackage.exe" /Action:Export /ssn:SERVER_NAME_HERE /sdn:DATABASE_NAME /su:USERNAME /sp:PASSWORD /tf:C:\Users\William\Desktop\BackupTest\BACKUPFILE'%date%'.bacpac
ERROR
*** Unrecognized command line argument '23/06/2017'.bacpac'.
I have tried using
+%date%+
+%date
And other options but no luck. Can anyone suggest anything?

More fundamentally, it is not recommend using bacpac to backup database. Bacpac is for load & move data in and out of Azure on demand.
SQLDB on Azure has backup service on by default so a scheduled backup is already provided by the service.
In addition, to properly make a bacpac, the database needs to be copied first then make a bacpac from the copy. Otherwise transactional consistency is not guaranteed and importing the bacpac can fail in the worst case.

You can add it using PowerShell as explained on below example.
Param(
[Parameter(Position=0,Mandatory=$true)]
[string]$ServerName
)
cls
try {
if((Get-PSSnapin -Name SQlServerCmdletSnapin100 -ErrorAction SilentlyContinue) -eq $null){
Add-PSSnapin SQlServerCmdletSnapin100
}
}
catch {
Write-Error "This script requires the SQLServerCmdletSnapIn100 snapin"
exit
}
$script_path = Split-Path -Parent $MyInvocation.MyCommand.Definition
$sql = "
SELECT name
FROM sys.databases
WHERE name NOT IN ('master', 'model', 'msdb', 'tempdb','distribution')
"
$data = Invoke-sqlcmd -Query $sql -ServerInstance $ServerName -Database master
$data | ForEach-Object {
$DatabaseName = $_.name
$now=get-Date
#
# Run sqlpackage
#
&"C:\Program Files (x86)\Microsoft SQL Server\110\DAC\bin\sqlpackage.exe" `
/Action:extract `
/SourceServerName:$ServerName `
/SourceDatabaseName:$DatabaseName `
/TargetFile:$script_path\DACPACs\$DatabaseName$now.dacpac `
/p:ExtractReferencedServerScopedElements=False `
/p:IgnorePermissions=False
}
Hope this helps.
Regards,
Alberto Morillo
SQLCoffee.com

Related

How to display the output (query result) from an update query which is executed by a powershell script?

On a daily basis, I need to update certain number of records in a DB.
Now to update this DB, I am using Merge --> Select--> Update sequentially.
But I need to display the output from this update statement (in a log file)
Code: update_status.ps1
$FilePath = $HOME+"\bin\ORACLE_CONNECTION_HOME\oracle_config.properties"
$SID=Select-String -Pattern "oracle_SID" -Path $FilePath
$Data_Source=$SID.ToString().split('=')[1]
$user_name=Select-String -Pattern "oracle_user_name" -Path $FilePath
$User=$user_name.ToString().split('=')[1]
$user_password=Select-String -Pattern "oracle_user_password" -Path $FilePath
$Pwd=$user_password.ToString().split('=')[1]
$connectionString= "Data Source=$Data_Source;User Id=$User;Password=$Pwd;Integrated Security=no"
[System.Reflection.Assembly]::LoadWithPartialName("System.Data.OracleClient") | Out-Null
$connection = New-Object System.Data.OracleClient.OracleConnection($connectionString)
function Oracle_Connection ( $query)
{
$connectionString= "Data Source=$Data_Source;User Id=$User;Password=$Pwd;Integrated Security=no"
[System.Reflection.Assembly]::LoadWithPartialName("System.Data.OracleClient") | Out-Null
$connection = New-Object System.Data.OracleClient.OracleConnection($connectionString)
$queryString = $query
$command = New-Object System.Data.OracleClient.OracleCommand($queryString, $connection)
$connection.Open()
$dataset = New-Object System.Data.DataTable
$oracleadapter = New-Object System.Data.OracleClient.OracleDataAdapter $command
$resultcount = $oracleadapter.fill($dataset)
$result = $command.ExecuteScalar()
Write-Host $result
$connection.Close()
}
function Update_p2c ($p2c, $c2p)
{
Write-Host "Updating P2C"
$query_sub_p2c ="MERGE INTO TABLE TB USING (SELECT ...) src ON ( NAME = src.NAME) WHEN MATCHED THEN UPDATE SET TB.P2C = src.ID";
Oracle_Connection $query_p2c
if ($resultcount -gt 0) { Write-Host "$resultcount rows were updated"} else {Write-Host "No rows were updated"}
}
##Initial setup completed.
#Defining Source and Target variables used in functions
$p2c = 'P2C'
$c2p = 'C2P'
Update_p2c -p2c $p2c -c2p $c2p
##End
Result:
PS D:\
Updating P2C
No rows were updated
However, I see that when I run the select & update manually in DB, I can see the rows getting selected as well as updated respectively.
This script is triggered by a .bat file in a task scheduler and it generates a log file
bat file:
pushd "%~dp0"
start /B /WAIT powershell -File "D:\bin\update_status.ps1" >> D:\log\update_status_%USERNAME%_%date%_log.log 2>&1
exit
My requirement is: I need to get the output from the update ( so and so rows updated from db) into the log file. Even if no rows get updated, it should show the same.
Please let me know if my ask is not clear.
Any help would be greatly appreciated :)
In principle you do not need a batch to run a powershell script from the task scheduler.
Task -> Actions -> Program/script = powershell.exe
Task -> Actions -> Add arguments = -File "D:\bin\update_status.ps1"
If the data you are looking for ist stored in the variable $result, simply wirte it to the logfile:
$result | set-content "D:\log\update_status_$($env:username)_$(get-date -format 'yyyy-dd-MM')_log.log"
Note $env:username will always be the caller identity of the scheduled task.

Disable Computers with Powershell

I have a list of 150 computers I would like to disable in active directory with powershell.
So I have a csv file with the computernames and the follwoing script:
Import-Module ActiveDirectory
$computers = import-csv "C:\temp\test.csv"
foreach ($computer in $computers)
{
$computer | disable-adaccount
$computer | move-adobject -targetpath "OU=Disabled computers, DC=domain, DC=com"
}
Following error occures:
Disable-ADAccount : The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of the parameters that take pipeli
ne input.
Can someone please help?
Cheers
Try a regular parameter instead of a pipe: disable-adaccount $computer
(If necessary repeat this for the other call)
Also have a look at this question at Technet: Powershell script to disable AD account based on CSV file
Create a txt file called disable.txt and put list of computers that u want to disable on C:\temp location
Run this script:
$Computer = Get-content c:\temp\disable.txt
Foreach ($Computer in $computers) {
Set-ADComputer -Identity $computer -Enabled $false
}
I know this is an old post but I believe it's a type mismatch of Disable-ADAccount expecting a property rather than the whole object for input.
Using the example I added .DistinguishedName to the item passed to Disable-ADAccount and it worked. The input csv would need to have DistinguishedName available to the command though.
{
Disable-ADAccount $StaleComputer.DistinguishedName
Move-ADObject $StaleComputer.DistinguishedName -TargetPath "OU=Disabled Computers, DC=domain, DC=com"
}

How to map Network Printers with PowerShell and CSV

I want to deploy our Network Printers that are shared from a Print-Server to Windows 10 PCs, on per-machine basis.
Currently we do this with a Kix-Script and ini file, but I want to move this to PowerShell and deploy it as a Startup/Login Script with Group Policy. The deployment must be with PowerShell not purely GPO, with a script we are more flexible to deploy to singular machines.
I've written a PS Script and using a CSV File containing the PCs and Printers to map, but it seams completely wrong. Is there a better way to deploy the printers?
Here are my CSV, 'True' is to set Printer as Default:
#TYPE Selected.System.Management.ManagementObject.Data.DataRow
Name
PC0001
\\SV0002\PR0001, True
\\SV0002\PR00002
Name
PC0002
\\SV0002\PR0001, True
\\SV0002\PR00002​
and the PS-Script:
Get–WMIObject Win32_Printer | where{$_.Network -eq ‘true‘} | foreach{$_.delete()}
$Printers=IMPORT-CSV \\server\$env:username\printers.csv
FOREACH ($Printer in $Printers) {
Invoke-Expression 'rundll32 printui.dll PrintUIEntry /in /q /n $($Printer.Name)'
}​
I edited the csv File, and it looks like this now:
Client;1;2;3;4;5;6;7;8;9;10;11;12;13;14;15;Default
PC0001;\\SV0001\PR0001;\\SV0001\PR0002;;;;;;;;;;;;;;pr_01
PC0002;\\SV0001\PR0001;\\SV0001\PR0002;\\SV0001\PR0003;;;;;;;;;;;;;pr_03
We did that with Excel, so it's easier to edit, and save it as csv.
Also where is located, we changed it to \Server\Netlogon\Subfolder\Printers.csv so that also the the Variable is changed to:
$Printers=IMPORT-CSV \\server\Netlogon\Subfolder\printers.csv
But now I think the whole script is wrong?
Using a CSV like this:
name,printers,defaultprinter
PC0001,\\SV0001\PR0001;\\SV0001\PR0002,PR0002
PC0002,\\SV0001\PR0001;\\SV0001\PR0003,PR0003
PC0003,\\SV0001\PR0001;\\SV0001\PR0004,PR0004
The code would be:
$csv = "\\server\Netlogon\Subfolder\printers.csv"
$Computers = Import-Csv $csv
foreach ($Computer in $Computers){
If ($Computer.name -eq $env:computername) {
$Printers = ($Computer.printers).split(";")
foreach ($Printer in $Printers) {Add-Printer $Printer -ErrorAction SilentlyContinue}
(New-Object -ComObject WScript.Network).SetDefaultPrinter("$($Computer.defaultprinter)")
}
}
The way we do (did) it here at work was by invoking some VBScript from within the PowerShell script.
Print server and Printer are obtained via AD cmdlets.
$net = New-Object -Com WScript.Network
$net.AddWindowsPrinterConnection("\\" + $PRINT_SERVER + "\" + $PRINTER)
Starting from Windows 8 :
# Add the printer
Add-Printer -ConnectionName ("\\" + $printServer + "\" + $printerName) -Name $printerName
# Get the printer
$printer = Get-WmiObject -Query "Select * From Win32_Printer Where ShareName = '$printerName'"
# Set printer as default
$printer.SetDefaultPrinter()
I solved the Problem with the Script of James C., many thanks to him, it was a big help!.
The only wrong Thing was that between Add-Printer and $Printer, it had to be -ConnectionName. After that Little Edit in the script, everything was fine.
So we made a GP_Printers, where we putted under Computer Configuration/Windows Settings/Scripts/Startup this Script as printermapping.ps1
Also we putted into Shutdown a PowerShell Script where all Printer Connection are deleted.
Here are all the scripts.
CSV:
name,printers,defaultprinter
PC0001,\\SV0001\PR0001;\\SV0001\PR0002,PR0002
PC0002,\\SV0001\PR0001;\\SV0001\PR0003,PR0003
PC0003,\\SV0001\PR0001;\\SV0001\PR0004,PR0004
Printer Mappings with PowerShell depending on CSV:
$csv = "\\server\Netlogon\Subfolder\printers.csv"
$Computers = Import-Csv $csv
foreach ($Computer in $Computers){
If ($Computer.name -eq $env:computername) {
$Printers = ($Computer.printers).split(";")
foreach ($Printer in $Printers) {Add-Printer-ConnectionName $Printer -ErrorAction SilentlyContinue}
(New-Object -ComObject WScript.Network).SetDefaultPrinter("$($Computer.defaultprinter)")
}
}
And the Printer Disconnection:
Get-WmiObject -Class Win32_Printer | where{$_.Network -eq ‘true‘}| foreach{$_.delete()}
I hope this could be helpfoul for others.
Again many thanks to James C.
WBZ-ITS
I've made some correction and improvements to the script, and found also some Problem that Comes if you use it on a GPO, the changes are following:
CSV:
name,printers,defaultprinter
PC0001,\\SV0001\PR0001;\\SV0001\PR0002,PR0002
PC0002,\\SV0001\PR0001;\\SV0001\PR0003,PR0003
PC0003,\\SV0001\PR0001;\\SV0001\PR0004,PR0004
The Connection Script:
$csv = "\\server\Netlogon\Subfolder\printers.csv"
$Computers = Import-Csv $csv
foreach ($Computer in $Computers){
If ($Computer.name -eq $env:computername) {
$Printers = ($Computer.printers).split(";")
foreach ($Printer in $Printers) {Add-Printer-ConnectionName $Printer -ErrorAction SilentlyContinue}
(New-Object -ComObject WScript.Network).SetDefaultPrinter("$($Computer.defaultprinter)")
}
}
And also a disconnect Script when logging off:
#$a = Get-WMIObject -query "Select * From Win32_Printer Where Name = 'Microsoft Print to PDF'"
#$a.SetDefaultPrinter()
$TargetPrinter = "Microsoft Print to PDF"
$ErrorActionPreference = “SilentlyContinue”
$LocalPrinter = GWMI -class Win32_Printer | Where {$_.Name -eq $TargetPrinter}
$LocalPrinter.SetDefaultPrinter()
$ErrorActionPreference = “Stop”
Get-WmiObject -Class Win32_Printer | where{$_.Network -eq ‘true‘}| foreach{$_.delete()}
To disconnect the default printer must be changed, otherwise it won't be disconnected.
After all Script was made, we putted them in a GPO under User Configuration\Policies\Windows Settings\Scripts and there on Logon and Logoff.
You may have some troubles that the GPOs won't run, so here some usefull troubleshooting guides that i found:
The Scripts aren't working as Machine Policies under Startup and Shutdown, they have to be in the User Configuration as mentioned above.
Also you have to configure the Policie that deley the Script of 5 minutes. These are under Computer Configuration\Administrative Templates\System\Group Policy\Configure Logon Script Delay aktivate them and set the delay to 0 minutes, otherwise any Script will be deleyed to 5 minutes after logon.
Also a problem could be, if you are running the GPO on Windows 8/10 System, and you made them on a WIndows 7 PC. Create GPOs allways on the Server 2008/R2 or 2012R2 for this kind of system.
It could be helpfoul also if you configure the Logon/Logoff GPO as follows: As Scriptname "powershell.exe" (without quotes) and as Script Parameters -F "\SERVER\FREIGABE\meinskript.ps1" (with quotes.
I hope this could help someone else.
Thanks to who hleped me.
WBZ-ITS

Publishing DacPacs in Visual Studio 2013

I have an SSDT database project in Visual Studio 2013. This is used as the "answer sheet" when publishing database updates to a database in the other environments. I recently came across Jamie Thompson's blog article on DacPacs, where he writes a great summary on what DacPacs are, and how to use them.
Now, say I have the following scenario:
The SSDT project in VS2013, which is version 1.0.33
A database in my Dev environment, which is version 1.0.32
A database in my S-test environment, whic is version 1.0.31
According to Jamie, publishing databases changes using DacPacs is idempotent, i.e. I can publish the DacPac from the SSDT project in bullet 1 to the database in bullet 3, and it will get all the changes done to the database in both version 1.0.32 and 1.033 since the DacPac contains information about the entire DB schema (which then also should include changes done in version 1.0.32).
Is this a correct understanding of how publishing a DacPac works?
Yes, once you defined your model in a DACPAC in a declarative way, you can then deploy your model to any target environment with whatever version of you database.
The engine will automatically generate the proper change scripts according to the target.
You can deploy (publish) your model from Visual Studio or from command line using the SqlPackage.exe utility. Here an example of a PowerShell script that use SqlPackage.exe and a Publish Profile file. You can choose to publish directly or generate the change script (set the $action variable). The DACPAC file and the Publish Profile file have to be in the same folder of the ps file. A log file will be generated:
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
####################################
$action = 'Publish' #Only generate script: 'Script'; Publish directly: 'Publish'
$databaseName = 'Test'
$serverName = 'localhost'
$dacpacPath = Join-Path $scriptPath '.\Test\bin\Debug\Test.dacpac'
$publishProfilePath = Join-Path $scriptPath '.\Test\Scripts\Publish\Test.publish.xml'
$outputChangeScriptPath = Join-Path $scriptPath 'TestDeploymentScript.sql'
$logPath = Join-Path $scriptPath 'TestDeployment.log'
####################################
$sqlPackageExe = 'C:\Program Files (x86)\Microsoft SQL Server\110\DAC\bin\SqlPackage.exe'
if ($action.ToUpper() -eq 'SCRIPT')
{
Write-Host '********************************' | Tee-Object -File "$logPath"
Write-Host '* Database Objects Scripting *' | Tee-Object -File "$logPath"
Write-Host '********************************' | Tee-Object -File "$logPath"
$args = "/Action:Script /TargetDatabaseName:$databaseName /TargetServerName:$serverName " +
"/SourceFile:""$dacpacPath"" /Profile:""$publishProfilePath"" /OutputPath:""$outputChangeScriptPath"" "
$command = "& ""{0}"" {1}" -F $sqlPackageExe, $args
Invoke-Expression $command | Tee-Object -File "$logPath"
if($LASTEXITCODE -ne 0)
{
$commandExitCode = $LASTEXITCODE
$Error[0] | Tee-Object -File $outputChangeScriptPath
return $commandExitCode
}
}
if ($action.ToUpper() -eq 'PUBLISH')
{
# DWH
Write-Host '*********************************' | Tee-Object -File "$logPath"
Write-Host '* Database Objects Deployment *' | Tee-Object -File "$logPath"
Write-Host '*********************************' | Tee-Object -File "$logPath"
$args = "/Action:Publish /TargetDatabaseName:$databaseName /TargetServerName:$serverName " +
"/SourceFile:""$dacpacPath"" /Profile:""$publishProfilePath"" "
$command = "& ""{0}"" {1}" -F $sqlPackageExe, $args
Invoke-Expression $command | Tee-Object -File "$logPath"
if($LASTEXITCODE -ne 0)
{
$commandExitCode = $LASTEXITCODE
$Error[0] | Tee-Object -File $outputChangeScriptPath
return $commandExitCode
}
}

Determine local path of a TFS Workspace via tf.exe

I am using a batch script for getting the latest version of specific projects. This script only runs tf.exe and gets the latest version of some Binaries. Everything works fine, but I would like to change the attrib of the downloaded files to be writeable (by deafult these files are read-only). For that I want to determine the local path of the files and use the attrib-command from batch.
tf.exe workfold [Workspace] shows me the local path in some kind of listing but it would be easier, if it only shows me what I want so I can use the prompt. Until now the it looks like this:
tf.exe workfold [Workspace]
=======================================
Arbeitsbereich: XYZ-xxxxxx (Username)
Auflistung: TFS-URL
[Workspace]: C:\xxx\TFS\xxx
Is it possible to determine only the local path mapping of a TFS Workspace so that I can use the prompt for the attrib-command without parsing?
What about the following (crude!!!) concept?
function Get-TfsWorkfold([string]$TfsCollection, [string]$TfsWorkspace)
{
$TfExePath = "${env:ProgramFiles(x86)}\Microsoft Visual Studio 10.0\Common7\IDE\TF.exe"
Write-Output "Getting workfold for '$TfsCollection'->'$TfsWorkspace'..."
Push-Location $LocalPath
& "$TfExePath" workfold /collection:$TfsCollection /workspace:$TfsWorkspace
}
function Handle-Path()
{
param([Parameter(ValueFromPipeline=$true,Position=0)] [string] $line)
$startIndex = $line.IndexOf(': ') + 2;
$correctedLine = $line.subString($startIndex, $line.length - $startIndex - 1);
Write-Output $correctedLine;
Get-ChildItem $correctedLine
}
Get-TfsWorkfold "{serverAndcollection}" "{workspace}" > c:\temp\test.txt
Select-String c:\temp\test.txt -pattern:': ' | Select-Object Line | Handle-Path
The last line in Handle-Path is the example which you can rewirte with whatever you want to. It is PowerShell but it should work as you want.
Replace {serverAndcollection} and {workspace}.
Real men do it in one line
powershell -command "& {tf workfold | Select-String -pattern:' $' -SimpleMatch | Select-Object Line | ForEach-Object {$startIndex = $_.Line.IndexOf(': ') + 2; $_.Line.subString($startIndex, $_.Line.length - $startIndex - 1)}}"
Current answer will only return one last path if there are many.
You can also do it without any string manipulation, with calls to TF.exe. I have wrapped that in PowerShell scripts, so you get the following:
function Add-TfsTypes
{
# NOTE: Not all of the below are needed, but these are all the assemblies we load at the moment. Please note that especially NewtonSoft dll MUST be loaded first!
$PathToAssemblies = "C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer"
Add-Type -Path "$PathToAssemblies\NewtonSoft.Json.dll"
Add-Type -Path "$PathToAssemblies\System.Net.http.formatting.dll"
Add-Type -Path "$PathToAssemblies\Microsoft.TeamFoundation.Client.dll"
Add-Type -Path "$PathToAssemblies\Microsoft.TeamFoundation.Common.dll"
Add-Type -Path "$PathToAssemblies\Microsoft.TeamFoundation.VersionControl.Client.dll"
Add-Type -Path "$PathToAssemblies\Microsoft.TeamFoundation.WorkItemTracking.Client.dll"
}
function Get-TfsServerPathFromLocalPath {
param(
[parameter(Mandatory=$true)][string]$LocalPath,
[switch]$LoadTfsTypes
)
if ($LoadTfsTypes) {
Add-TfsTypes # Loads dlls
}
$workspaceInfo = [Microsoft.TeamFoundation.VersionControl.Client.Workstation]::Current.GetLocalWorkspaceInfo($LocalPath)
$server = New-Object Microsoft.TeamFoundation.Client.TfsTeamProjectCollection $workspaceInfo.ServerUri
$workspace = $workspaceInfo.GetWorkspace($server)
return $workspace.GetServerItemForLocalItem($LocalPath)
}
The above method can then be called like this:
$serverFolderPath = Get-TfsServerPathFromLocalPath $folderPath -LoadTfsTypes
$anotherServerPath = Get-TfsServerPathFromLocalPath $anotherItemToTestPathOn

Resources