MSDeploy API - Deleting a remote file through code - msdeploy

I am looking to remotely fire a delete command using the MSDeploy API through c# code.
I want to achieve the following command:
msdeploy.exe -verb:delete -dest:contentPath="/folderName/filename.txt"
instead of through running an unmanaged external executable, I want to execute this using the MSDeploy .Net API.

Assuming you're trying to delete an absolute filepath (rather than a file in a website), you're looking for something like this:
DeploymentObject destObject = DeploymentManager.CreateObject(
DeploymentWellKnownProvider.FilePath, "/foldername/filename.txt");
DeploymentObject sourceObject = DeploymentManager.CreateObject("auto", "");
DeploymentBaseOptions baseOptions = new DeploymentBaseOptions();
DeploymentSyncOptions syncOptions = new DeploymentSyncOptions
{
DeleteDestination = true;
};
DeploymentChangeSummary results = sourceObject.SyncTo(
destObject, baseOptions, syncOptions);
// results.ObjectsDeleted == 1

I've found the answer thanks to Richard Szalay's leading and i've used the ContentPath provider as this is a common provider used by VS Publishing so the chances of having permissions is high:
var deployBaseOptions = new DeploymentBaseOptions
{
ComputerName = "https://mywebserver.com:8172/msdeploy.axd?sitename=yourIISWebsiteName",
UserName = "username",
Password = "password",
UseDelegation = true,
AuthenticationType = "Basic"
};
var syncOptions = new DeploymentSyncOptions
{
DeleteDestination = true
};
var deploymentObject = DeploymentManager.CreateObject(DeploymentWellKnownProvider.ContentPath,
"yourIISWebsiteName" + "/fileToDelete.txt",
destBaseOptions);
var results = deploymentObject.SyncTo(deployBaseOptions, syncOptions);
The weird thing is that results always shows 3 files deleted even when there is only one...?!

Related

Azure Resource Manager DNS: Sample code to create a DNS record

I'm currently trying to move out from using old Microsoft.Azure.Management.Dns package to the new Azure.ResourceManager.Dns.
However I've been having issues in our code that creates Dns records such as an Arecord.
I've tried to go through the official documentation https://learn.microsoft.com/en-us/dotnet/api/azure.resourcemanager.dns.dnsarecordcollection.createorupdate?view=azure-dotnet
But the classes that represent an Arecord are either read only or private so I have no idea how to update this simple lines:
RecordSet set = DnsManagementClient.client.RecordSets.Get(resourceGroupName, zone, recordSetName, RecordType.A);
set.ARecords = set.ARecords ?? new List<ARecord>();
set.ARecords.Add(new ARecord(ipAddress));
DnsManagementClient.client.RecordSets.CreateOrUpdateWithHttpMessagesAsync(resourceGroupName, zone, recordSetName, RecordType.A, set, ifNoneMatch: "*");
Currently documentation only talks about Zones, can an example be added to the official documentation on how to add or update a DNS record (A,CNAME,etc..)
https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/dns/Azure.ResourceManager.Dns
I'm expecting a method to create an A record that let's you specify an IP address, and currently all the classes that potentially can be used to do that are either read-only or internal.
DnsARecordData has an internal list of Arecords, DnsARecordData.DnsARecords is where we can invoke the Add method to create the record. The reason DnsARecordData doesn't have a setter method is due to the .Net framework design guideline..
An example of how to create an A record using Azure.Resourcemanager.Dns can be found here:
// Create or update A record
string myARecordName = "myrecord";
DnsARecordData dnsARecordData = new() {TtlInSeconds = (long)TimeSpan.FromHours(1).TotalSeconds};
dnsARecordData.DnsARecords.Add(new DnsARecordInfo { IPv4Address = IPAddress.Parse("127.0.0.1") });
DnsARecordCollection dnsARecordCollection1 = dnsZoneResource.GetDnsARecords();
dnsARecordCollection1.CreateOrUpdate(WaitUntil.Completed, myARecordName, dnsARecordData);
// Create or update CName pointing to A record
string myCnameName = "mycname";
DnsCnameRecordData dnsCnameRecordData = new() { Cname = $"{myARecordName}.{DnsZone}", TtlInSeconds = (long)TimeSpan.FromMinutes(10).TotalSeconds, };
DnsCnameRecordCollection cnameRecordCollection = dnsZoneResource.GetDnsCnameRecords();
cnameRecordCollection.CreateOrUpdate(WaitUntil.Completed, myCnameName, dnsCnameRecordData);
I tried in my environment and got below results:
You can create A record set using Azure.ResourceManager.Dns package. The version of NuGet package is beta-1.
NuGet Package:
Azure.ResourceManager.Dns 1.0.0 beta-1
Code:
using Azure;
using Azure.Identity;
using Azure.ResourceManager;
using Azure.ResourceManager.Dns;
using Azure.ResourceManager.Resources;
using System.Net;
ArmClient armClient = new ArmClient(new DefaultAzureCredential());
SubscriptionResource subscription = await armClient.GetDefaultSubscriptionAsync();
// first we need to get the resource group
string rgName = "rg-name";
ResourceGroupResource resourceGroup = await subscription.GetResourceGroups().GetAsync(rgName);
string dnsZoneName = "dns name";
DnsZoneCollection dnsZoneCollection = resourceGroup.GetDnsZones();
DnsZoneData data1 = new DnsZoneData("Global")
{
};
ArmOperation<DnsZoneResource> lro = await dnsZoneCollection.CreateOrUpdateAsync(WaitUntil.Completed, dnsZoneName, data1);
DnsZoneResource dnsZone = lro.Value;
RecordSetACollection recordSetACollection = dnsZone.GetRecordSetAs();
string name = "cname1";
var parm = new ARecordSetData();
parm.TTL =600;
parm.ARecords = new List<ARecord>();
parm.ARecords.Add(new ARecord("1.2.3.4"));
ArmOperation<RecordSetAResource> recordSetAResource = recordSetACollection.CreateOrUpdate(WaitUntil.Completed, name,parm);
RecordSetAResource recordSetAs = recordSetAResource.Value;
Console:
Portal:
For more reference:
azure-sdk-for-net/Sample2_ManagingRecordSetPtrs.md at dvbb-mgmt-track2-dns-2 ยท dvbb/azure-sdk-for-net (github.com)

Dynamic FTP Folder pipeline

I'm trying to set Dynamically the output folder of an FTP location.
Assignment, for each customer I need to create a separate folder to store an Excel file and / or XML file.
What I've tried
Created a Custom Pipeline Component to set all the required Properties into a FTP send port.
Tried the same pipeline into a Dynamic Send Port
For testing tried the code in an Orchestration.
What I've noticed:
When I send through the FTP Send Port the properties won't be overridden by the custom pipeline properties.
When I send through the Dynamic I always get the following error
A failure was encountered while transmiting the message
Even when I'm trying to set the properties into the Orchestration I get the same error.
Also when I'm trying to send through the Dynamic Send Port I notice that the pipeline component is not touched.
Execute code part of the custom pipeline component
public IBaseMessage Execute(IPipelineContext pipelineContext, IBaseMessage inputMessage)
{
Guid callToken = TraceManager.PipelineComponent.TraceIn(CLASSNAME + ".Execute() - Start", pipelineContext.PipelineID, pipelineContext.PipelineName, pipelineContext.StageID);
if (!this.Active)
{
TraceManager.PipelineComponent.TraceOut(callToken, CLASSNAME + ".Execute() - Pipeline component is not active!");
return inputMessage;
}
try
{
string completeFTPUri = null;
string fileName = null;
string accountNumber = Convert.ToString(inputMessage.Context.Read(PROP_ACCOUNTNUMBER.Name.Name, PROP_ACCOUNTNUMBER.Name.Namespace));
if (!string.IsNullOrWhiteSpace(accountNumber))
this.Folder = string.Format("{0}/{1}", this.Folder, accountNumber);
if (!string.IsNullOrWhiteSpace(this.Folder))
completeFTPUri = string.Format("ftp://{0}:21/{1}", this.FTPUri, this.Folder);
else
completeFTPUri = this.FTPUri;
if (!UseDefaultFilename)
{
string receiveFilename = null;
receiveFilename = Convert.ToString(inputMessage.Context.Read(FTP_RECEIVED_FILENAME.Name.Name, FTP_RECEIVED_FILENAME.Name.Namespace));
if (!string.IsNullOrWhiteSpace(receiveFilename))
fileName = Path.GetFileName(receiveFilename);
}
if (string.IsNullOrWhiteSpace(fileName))
{
if (string.IsNullOrWhiteSpace(this.Filename))
fileName = DEFAULT_FILENAME;
else
fileName = this.Filename;
}
if (fileName.Contains("{0") || fileName.Contains("{1"))
{
fileName = string.Format(fileName, DateTime.Now, inputMessage.MessageID);
}
if (!string.IsNullOrWhiteSpace(this.Folder))
{
//inputMessage.Context.Write(FTP_BEFORE_PUT.Name.Name, FTP_BEFORE_PUT.Name.Namespace, string.Format("MKDIR {0}", string.Format("ftp://{0}:21/{1}", this.FTPUri, this.Folder)));
inputMessage.Context.Promote(FTP_BEFORE_PUT.Name.Name, FTP_BEFORE_PUT.Name.Namespace, string.Format("MKDIR {0}", completeFTPUri));
}
//inputMessage.Context.Write(OUTBOUND_TRANSPORT_LOCATION.Name.Name, OUTBOUND_TRANSPORT_LOCATION.Name.Namespace, completeFTPUri);
//inputMessage.Context.Write(FILE_RECEIVED_FILENAME.Name.Name, FILE_RECEIVED_FILENAME.Name.Namespace, fileName);
//inputMessage.Context.Write(FTP_USERNAME.Name.Name, FTP_USERNAME.Name.Namespace, _userName);
//inputMessage.Context.Write(FTP_PASSWORD.Name.Name, FTP_PASSWORD.Name.Namespace, _password);
inputMessage.Context.Promote(OUTBOUND_TRANSPORT_LOCATION.Name.Name, OUTBOUND_TRANSPORT_LOCATION.Name.Namespace, completeFTPUri);
inputMessage.Context.Promote(OUTBOUND_TRANSPORT_TYPE.Name.Name, OUTBOUND_TRANSPORT_TYPE.Name.Namespace, "FTP");
inputMessage.Context.Promote(FILE_RECEIVED_FILENAME.Name.Name, FILE_RECEIVED_FILENAME.Name.Namespace, fileName);
inputMessage.Context.Promote(FTP_USERNAME.Name.Name, FTP_USERNAME.Name.Namespace, this.UserName);
inputMessage.Context.Promote(FTP_PASSWORD.Name.Name, FTP_PASSWORD.Name.Namespace, this.Password);
}
catch (Exception ex)
{
TraceManager.PipelineComponent.TraceError(ex, false, callToken);
throw new Exception(CLASSNAME + ".Execute() - Failed to set the filename.", ex);
}
TraceManager.PipelineComponent.TraceOut(callToken, CLASSNAME + ".Execute() - Finished.");
return inputMessage;
}
EDIT:
After trying a lot this morging an update.
When I try to Send Dynamicly through the Static SendPort I keep the same issue.
When I try to Send Dynamicly through a Dynamic SendPort I'm getting different error:
Inner exception: The value assigned to property 'Microsoft.XLANGs.BaseTypes.Address' is not valid: 'FTP URI'.
I don't know what the best solution is to resolve this issue.
I can also write everything to a helper class en try to send through C# code. But I want to use the force of BizTalk and want to be able to enable en disable ports when necessary. That's the main reason.
I'm not afraid to write custom pipeline components or somthing else, so if someone could help. PLEASE
Code of the Message Assign of the Orchestration
MsgPublishArticleMessage = MsgFullArticleMessage;
MsgPublishArticleMessage(*) = MsgFullArticleMessage(*);
MsgPublishArticleMessage(DOMAIN.BizTalk.Common.Schemas.Domain) = "ArticleMessage";
MsgPublishArticleMessage(DOMAIN.BizTalk.Common.Schemas.Service) = "PricatService";
MsgPublishArticleMessage(DOMAIN.BizTalk.Common.Schemas.Action) = "PublishPricatXLSX";
MsgPublishArticleMessage(DOMAIN.BizTalk.Common.Schemas.Version) = "1.0";
MsgPublishArticleMessage(DOMAIN.BizTalk.Common.Schemas.AccountNumber) = articleMessageRequest.AccountNumber;
MsgPublishArticleMessage(BTS.OutboundTransportLocation) = "ftp://URI:21/Pricat/" + articleMessageRequest.AccountNumber;
MsgPublishArticleMessage(BTS.OutboundTransportType) = "FTP";
MsgPublishArticleMessage(FTP.Password) = "********";
MsgPublishArticleMessage(FTP.UserName) = "UserName";
MsgPublishArticleMessage(FTP.BeforePut) = "MKDIR " + articleMessageRequest.AccountNumber;
MsgPublishArticleMessage(FTP.ReceivedFileName) = Destil.BizTalk.ArticleMessage.Components.OrchestrationHelper.CreateReceivedFileName(articleMessageRequest, ".xlsx");
PublishArticleMessagePort(Microsoft.XLANGs.BaseTypes.Address) = "FTPURI";
PublishArticleMessagePort(Microsoft.XLANGs.BaseTypes.TransportType) = "FTP";
MsgPublishArticleMessage(BTS.IsDynamicSend) = true;
EDIT 2:
When I change the Message Assingment to below code I can send the file to a dynamic folder.
The only problem I'm running into now:
When the Folder already exist I'm getting a failure.
Does anyone know what FTP command I need to use to create a Folder only if it don't exist?
I've try'ed the following commands
MDK -p /Pricat/AccountNumber;
MDK /Pricat/AccountNumber;
if not exist "/Pricat/AccountNumber" MDK /Pricat/AccountNumber
Changed code of message assign in the orchestration
MsgPublishArticleMessage = MsgFullArticleMessage;
MsgPublishArticleMessage(*) = MsgFullArticleMessage(*);
MsgPublishArticleMessage(DOMAIN.BizTalk.Common.Schemas.Domain) = "ArticleMessage";
MsgPublishArticleMessage(DOMAIN.BizTalk.Common.Schemas.Service) = "PricatService";
MsgPublishArticleMessage(DOMAIN.BizTalk.Common.Schemas.Action) = "PublishPricatXLSX";
MsgPublishArticleMessage(DOMAIN.BizTalk.Common.Schemas.Version) = "1.0";
MsgPublishArticleMessage(DOMAIN.BizTalk.Common.Schemas.AccountNumber) = articleMessageRequest.AccountNumber;
MsgPublishArticleMessage(BTS.OutboundTransportLocation) = "ftp://URI:21/Pricat/" + articleMessageRequest.AccountNumber;
MsgPublishArticleMessage(BTS.OutboundTransportType) = "FTP";
MsgPublishArticleMessage(FTP.Password) = "*********";
MsgPublishArticleMessage(FTP.UserName) = "username";
MsgPublishArticleMessage(FTP.BeforePut) = "MKD Pricat/" + articleMessageRequest.AccountNumber + "; CWD Pricat/" + articleMessageRequest.AccountNumber;
PublishArticleMessagePort(Microsoft.XLANGs.BaseTypes.Address) = "ftp://URI:21/" + DOMAIN.BizTalk.ArticleMessage.Components.OrchestrationHelper.CreateReceivedFileName(articleMessageRequest, ".xlsx");
PublishArticleMessagePort(Microsoft.XLANGs.BaseTypes.TransportType) = "FTP";
MsgPublishArticleMessage(BTS.IsDynamicSend) = true;
From the code snippet you have provided, can you check the below line.
PublishArticleMessagePort(Microsoft.XLANGs.BaseTypes.Address) = "FTPURI";
You have declared FTPURI as a variable and assigning a constant string to the address. This might explain the error -
Inner exception: The value assigned to property 'Microsoft.XLANGs.BaseTypes.Address' is not valid: 'FTP URI'.
When overwriting static send port properties you have to give adapter know that it should use message properties instead of port properties.
Set IsDynamicSend property to true
inmsg.Context.Promote("IsDynamicSend", "http://schemas.microsoft.com/BizTalk/2003/system-properties", true);

FluentMigrator create password protected SqlLite DB

I use FluentMigrator to create a SqlLite DB in C# using FluentMigrator.Runner.MigrationRunner. I wonder is there any way to use the SetPassword command o the SqlConnection only when the DB needs to be created ? There's a SqLiteRunnerContextFactory object but it don't seem to be a property that I can use to specify password.
public MigrationRunner CreateMigrationRunner(string connectionString, string[] migrationTargets, Assembly assembly, long version)
{
var announcer = new TextWriterAnnouncer(Console.WriteLine) { ShowSql = true };
var options = new ProcessorOptions { PreviewOnly = false, Timeout = 60 };
var runnerContext = new SqLiteRunnerContextFactory().CreateRunnerContext(connectionString, migrationTargets, version, announcer);
var sqlLiteConnection = new SQLiteConnection(connectionString);
//If the DB has already been created, it crashes later on if I specify this
sqlLiteConnection.SetPassword("ban4an4");
return new MigrationRunner(assembly,
runnerContext,
new SQLiteProcessor(sqlLiteConnection,
new SQLiteGenerator(),
announcer,
options,
new SQLiteDbFactory()));
}
I would like to avoid having to look if the file exists before setting password on connection.
Well, finally the code below works perfectly by using SetPassword everytime you create de runner. No need to check if the file exists or not. First time it creates it with the password and second time it opens it with it seems to use it to open DB. Which is exactly what I was looking for.

Visual Studio Team Services: Get all workitems using RestSharp and JSON.NET

I'm trying to get all workitems from Visual Studio Team Services (was Visual Studio Online). I've been able to create them from my application but having issues finding all of them. I have been trying to use the wiql but i dont seem to get it to work. Any help or hints are apreciated.
my code:
var Client = new RestClient("https://myvso.visualstudio.com/DefaultCollection/_apis/wit/wiql?api-version=1.0");
Client.Authenticator = new HttpBasicAuthenticator(username, password);
IRestRequest request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.RequestFormat = DataFormat.Json;
var query = JsonConvert.SerializeObject(new QueryModel() {query = "Select [System.Id] From WorkItems" });
request.AddJsonBody(query);
var response = Client.Execute(request);
The error i keep getting is:
"{\"count\":1,\"value\":{\"Message\":\"Error converting value \\\"{\\\"query\\\":\\\"Select [System.Id], From WorkItems\\\"}\\\" to type 'Microsoft.TeamFoundation.WorkItemTracking.Web.Models.Wiql'. Path '', line 1, position 52.\\r\\n\"}}"
Can someone give me a tip as to what is wrong with my query?
Try following:
Remove:
var query = JsonConvert.SerializeObject(new QueryModel() {query = "Select [System.Id] From WorkItems" });
Update:
request.AddJsonBody(new {query = "Select [System.Id] From WorkItems" });

start workflow using alfresco java script api or through web script

I want to start a workflow programatically. So written a web script.
Execute Script :
function startWorkflow()
{
var workflow = actions.create("start-workflow");
workflow.parameters.workflowName = "activiti$alfGroupReview";
workflow.parameters["bpm:workflowDescription"] = "Please review ";
workflow.parameters["bpm:groupAssignee"] = people.getGroup( "GROUP_site_collaborators");;
var futureDate = new Date();
futureDate.setDate(futureDate.getDate() + 7);
workflow.parameters["bpm:workflowDueDate"] = futureDate;
workflow.execute(document);
return ;
}
For the above script, I am getting error "document is not defined". I am referring https://forums.alfresco.com/en/viewtopic.php?f=34&t=42677 and http://livinginjava.blogspot.in/2008/10/starting-alfresco-workflow-using.html links.
So I update my script to :
function startWorkflow()
{
var nodeRef = "workspace://SpacesStore/25285e6c-2995-49fe-aa50-1270cefc806a";
var docNode = search.findNode(nodeRef);
var workflow = actions.create("start-workflow");
workflow.parameters.workflowName = "activiti$alfGroupReview";
workflow.parameters["bpm:workflowDescription"] = "Please review ";
workflow.parameters["bpm:groupAssignee"] = people.getGroup( "GROUP_aloha_collaborators");;
var futureDate = new Date();
futureDate.setDate(futureDate.getDate() + 7);
workflow.parameters["bpm:workflowDueDate"] = futureDate;
workflow.execute(docNode);
return ;
}
Here, nodeRef : is ref of a document from document library.
Now new error is :
500 Description: An error inside the HTTP server which prevented it from fulfilling the request.
Message: 06270056 Wrapped Exception (with status template): 06270273 Failed to execute script 'classpath*:alfresco/templates/webscripts/org/justransform/startWF.get.js': null
Exception: org.alfresco.scripts.ScriptException - 06270273 Failed to execute script 'classpath*:alfresco/templates/webscripts/org/justransform/startWF.get.js': null
org.alfresco.repo.jscript.RhinoScriptProcessor.execute(RhinoScriptProcessor.java:195)
thanks in advance.
Using Alfresco Workflow API.
Note: wfDocs holds the array of doc nodes:
// 2 days from now
var dueDate2d = new Date((new Date()).getTime() + 2*(24*60*60*1000));
// Start workflow
var wfdef = workflow.getDefinitionByName("activiti$alfGroupReview");
if (wfdef) {
var wfparams = new Array();
wfparams["bpm:workflowDescription"] = "Please review";
wfparams["bpm:groupAssignee"] = people.getGroup( "GROUP_site_collaborators");
wfparams['bpm:workflowDueDate'] = dueDate2d;
wfparams['bpm:workflowPriority'] = 1;
wfparams['wf:notifyMe'] = true;
var wfpackage = workflow.createPackage();
for each (var n in wfDocs)
wfpackage.addNode(n);
var wfpath = wfdef.startWorkflow(wfpackage, wfparams);
var tasks = wfpath.getTasks();
for each (task in tasks)
task.endTask(null);
}
This code runs fine if:
docNode is not null. You should add a check for this.
Your group exists. Probably worth adding a check for this.
The workflow exists with the ID specified. Use the workflow console to confirm this. For example, the ID your provided is not an
out-of-the-box workflow. If it is custom, maybe you haven't deployed
the workflow successfully or you have the ID incorrect.
Also, do not use a variable called "workflow". Alfresco already defines a root-scoped object called "workflow". Speaking of that, feel free to use the workflow JavaScript API to invoke your workflow instead of an action. Either should work, though.
I ran your code successfully using the JavaScript console and a workflow id of "activiti$activitiParallelGroupReview" (and after changing your workflow variable to workflowAct).

Resources