Tag Archives: vCO

Running free VeeamZip directly from the vSphere Web Client

veam_thumb.pngThere are a lot of times I find myself needing to take a one-off backup job of a VM – prior to software upgrades or patching I always like to take a backup of the affected VM(s) in the event that, well, you know, I mangle things.  VeeamZip is great for this – it allows me to process a quick backup of my VM that is separate from its’ normal backup and replication routines.  Since I deal in an environment that running paid Veeam licenses I have access to the Veeam Plug-in for the vSphere Web Client – and this plug-in does exactly what the title of this blog post is – it allows us to perform VeeamZips of our VMs without having to leave the vSphere Web Client and/or log into our Veeam Backup and Replication console.

What if I’m using Veeam Backup and Replication FREE?

So this is all great for me, but I got to thinking – What if I wasn’t running a paid version of Veeam Backup?  What if I was simply running the free version – this doesn’t come with Enterprise Manager, therefore it doesn’t come with a means of getting the Veeam Backup and Replication Web Client plug-in installed – therefore no VeeamZip from the Web Client right? – Wrong!  Ever since Veeam Backup and Replication v8 U2 came out they have been including PowerShell cmdlets around the VeeamZip functionality.  I wrote about how to use it last year in Scheduling Veeam Free Edition Backups.  Well, since we have PowerShell that means we can use vRealize Orchestrator to build a workflow around it – and we have the ability to execute workflows directly from within the vSphere Web Client – so without ado, Running the free VeeamZip functionality directly from the vSphere Web Client.

First up the script

I didn’t get too elaborate with the script as you can see below.  This is simply a handful lines that take in a few parameters; the VM to backup, the destination to store the backup, and the retention, or autodeletion of the backup.

1
2
3
4
5
6
7
8
9
10
11
12
Param(
[Parameter(Mandatory=$true)][string]$VM,
[Parameter(Mandatory=$true)][string]$Destination,
[Parameter(Mandatory=$true)][ValidateSet("Never","Tonight","TomorrowNight","In3days","In1Week","In2Weeks","In1Month")][string]$Autodelete
)
#Load Veeam Toolkit
& "C:\Program Files\Veeam\Backup and Replication\Backup\Initialize-VeeamToolkit.ps1"
#Get the VM Veeam Entity.
$vmentity = Find-VBRViEntity -Name $VM
 
#VeeamZip it!
Start-VBRZip -Entity $vmentity -Folder $destination -AutoDelete $Autodelete -DisableQuiesce

That’s it for the script – simple right – feel free to take this and add whatever you seem fit to suit your needs 🙂

The Orchestrator Configuration

Before we get to creating our workflow there are a few things we need to do within orchestrator, mainly adding our server that hosts our Veeam Free instance as a PowerShell host within vRO.  But even before we run the ‘Add a PowerShell Host’ workflow we need to run a few winrm commands on the Veeam Free instance.  I have a complete post about setting up a PowerShell host here, but will include the commands you need to run below for quick reference.

First up, on the Veeam server run the following in a command shell…

  • winrm quickconfig
  • winrm set winrm/config/service/auth @{Kerberos=”true”}
  • winrm set winrm/config/service @{AllowUnencrypted=”true”}
  • winrm set winrm/config/winrs @{MaxMemoryPerShellMB=”2048″}

Next, from within vRO (as shown below) we can run the the “Add a PowerShell host” workflow…

PowerShellHost1

As you can see my Veeam Server is actually the same as my vCenter server – don’t recommend doing this but hey it’s a small lab!  Just be sure to use the FQDN of your Veeam Server for Host/IP column.

PowerShellHost2

Ensure that the remote host type is WinRM, and that the Authentication method is set to Kerberos.

PowerShellHost3

And be sure that we are passing our username in the ‘username@domain’ format, along with ‘Shared Session’ for the session mode.  Once you are done go ahead and click ‘Submit’.  If everything goes as planned your Veeam Backup and Replication server should be added as a PowerShell host within vRealize Orchestrator.

And now, the workflow!

Finally we can get to actually building our workflow.  If you remember our script we take in three parameters; VM, Desitination and AutoDelete – so we will mimic the same with our workflow, only calling them Input Parameters within vRO (shown below)

workflow1

Now since we will be using the built-in Powershell workflow ‘Invoke an External Script’ we will also need to have some workflow attributes setup in order to pass to that workflow.  Below you can see how I’ve setup mine…

workflow2

Your configuration may vary a little from this one, but as you can see we simply add a PowerShell host attribute and map it to our newly added host, as well assign the ScriptPath attribute to the representation of where we saved our little VeeamZip script earlier.  The arguments attribute can remain empty as we will only use this to build the arguments string to pass to the script.

workflow3

The first element we want to add to our workflow schema is a Scriptable task – go ahead and drag that over into your workflow.  This is where we will create our arguments string.

workflow4

As far as what goes into the scripting you can see I’ve simply brought in the arguments attribute, along with our three input parameters and simply chained them together into one string (arguments = ‘”‘+VM.name+'” “‘+Destination+'” “‘+AutoDelete+'”‘;), then ensured that my arguments attribute was included in the output as well.

workflow5

Next drag the ‘Invoke an external script’ workflow into your schema (you can see I’ve renamed mine ‘Run VeeamZip’.  Ignore all of the prompts regarding the setup of parameters that pop up – the easiest way I like to do this is by editing the workflow (the pencil above it) and using the ‘Visual Binding’ tab as shown below.

workflow6

Simply drag and drop your in attributes to their corresponding in attributes on the external script workflow, along with mapping your output to output.  Easy Peasy!

At this point you can go ahead and save and close your workflow – we are done with Orchestrator.  If you want to run the workflow a few times to test from within vRO go ahead – but the point of this post was to run it from within the Web Client so let’s move on to that step.

vRO and vSphere Web Client

I love vRealize Orchestrator and I love the fact that I can contextually execute custom workflows from within the vSphere Web Client.  To do this you need to first register your vRO instance with vCenter – this should be automatically done for you depending on you set everything up – I’m not going to get into that configuration today.  To get to our context mappings we need to click on Home->vRealize Orchestrator.  With the vRO Home in context select the ‘Manage’ tab and then ‘Context Actions’.  We then are going to want to hit the little green + sign to add a new workflow context map.

webclientworkflow1

As far as the next steps they are pretty self explanatory – navigate through your vRO inventory to your workflow, click ‘Add’, and select ‘Virtual Machine’ from the types box.  This is what will allow us to right click on a VM and run our VeeamZip, passing the contextually selected VM to the workflow’s VM input parameter.  Click ‘OK’ and it’s time to VeeamZip!

Now when you want to run the workflow you can simply right click a VM and navigate to (in my case) All vRealize Orchestrator Actions->VeeamZipVM

execute1

As you can see our workflow will start, using our VM selected as the VM input, and simply prompt us for the destination and AutoDelete settings.

execute2

And there you have it!  We can now use the Free version of Veeam Backup and Replication to VeeamZip our VMs directly from within the vSphere Web Client.  In fact, our workflow will even show up within our vSphere tasks so we can monitor the status of the job.  Now, there is no error checking or anything like that…yet!  Let me know if you have any questions, concerns, etc… always happy to read the comments!

Silently installing Veeam v8 Update 2 – at scale with vRO

servletIt’s no surprise that most Veeam customers live with one or two Veeam Backup and Replication consoles – hey, it’s easier to manage, easier to report on, and easier to upgrade come update time if you keep your footprint to a minimum.  That said, I’m not most customers Smile  I have multiple Veeam B&R consoles – not necessarily because I have to, but it’s a preference – It allows me to split out functionality and services, I can have a self contained DR site, and I can also instantiate local backup functionality at multiple sites, leaving them to be non reliant on the main datacenter.  It’s working for me but there’s one caveat – come update time it’s a real big pain having to go out and click through a wizard 20 or so times.

Needless to say I was quite thrilled when I saw the following within the v8 update 2 release notes….

veeamsilent

w00t!  No more wizard driven day of click craziness for me.  After getting a little bit of help from Anton Gostev in the forums (if you have been in the Veeam forums you know this guy) I was off to the races in my automated install.

The command

Installing Veeam Update 2 silently is basically done in two steps; first we need to unpack the files from the installer and second we simply execute the HotFixRunner.exe file with the proper arguments.  Anton suggested placing both steps with at batch file so in the end I was left with something like such (setup.bat)…

1
2
3
mkdir c:\VeeamData\v8\unpacked
”c:\VeeamData\v8\VeeamBackup&Replication_8.0.0.2021_Update2.exe” /Q /C /T:c:\VeeamData\v8\unpacked
c:\VeeamData\v8\unpacked\HotFixRunner.exe silent noreboot log C:\VeeamData\v8\patch.log VBR_AUTO_UPGRADE=1

Basically save all the above within setup.bat, obviously changing your directory names to the where you copied the VeeamBackup&Replication_8.0.0.2021_Update2.exe file and where you want the files unpacked.  From there you simply execute setup.bat and sit back and relax while Veeam updates…

But that’s not enough for me

What about copying the update file out to the B&R servers?  What about copying setup.bat out?  We still need to do this and I for one don’t want to manually do anything Smile.  This could very easily be achieved with PowerShell or your tool of choice – but in the case I decided to lean on my good ol’ friend vRealize Orchestrator to do the trick.  Mainly because I’m sitting in the vSphere Web Client for a good chunk of the day and having the functionality right at my fingertips just seemed proper.  That, and every single one of my Veeam B&R servers are virtualized.  Another reason is because I’d like to expand on the workflow someday, giving it the ability to cancel any currently running Veeam jobs, report back on success, etc..  vRO through the use of PowerShell and REST plug-ins gives me all of this building-block functionality.  If your Veeam B&R console isn’t virtualized or you don’t want to automate with vRO go ahead and copy the files out using whatever tool you like and execute them – it’ll work just the same.

But if you want to dabble around in vRO or just want something a little more challenging go ahead and create a new workflow – The workflow I built performs three main functions; copies the update file, copies the setup file, and then executes the setup file – pretty simple.

As far as inputs and attributes this is the way I went about it.   For inputs I used only three, the VM name (this is the VBR instance I’ll be upgrading) and a username and password with permission to copy and execute on the file system.

veeamworkflowinputs

The rest of the information that the workflow needs will be stored in workflow attributes as it will always be static throughout all the installs and upgrades I’ll perform. In my case I used four attributes (shown below) defining the file paths on both the vRO server and the Veeam server for the setup.bat file and the Veeam update executable.

veeamvcoattr

Once these are defined it’s time to setup our workflow schema – Drag two scriptable tasks onto the schema editor and label them “Copy Files to VBR Server” and “Execute Setup.bat” or something more to your liking.

schema-vco

The Copy Files scriptable task handles the copying of both the Veeam update and setup.bat file.  Thankfully most the scripting for copying a file has already been completed and is stored inside a default vRO workflow titled “Copy File from vCO to Guest”.  I simply copied the script out of this workflow, pasted into my scriptable task and modified slightly to suit my needs.  You can see my final script along with a screen cap of the bindings so you can get a better understanding of which attributes/parameters need to be mapped into the scriptable task shown below.  If you run into some trouble, mainly permission issues have a look at this post by Nick Coyler which greatly helps with that issue.

vco-copyfiles

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var host = vm.sdkConnection;
 
var guestOperationsManager = host.guestOperationsManager;
var guestAuth = new VcNamePasswordAuthentication();
guestAuth.username = vmUsername;
guestAuth.password = vmPassword;
 
var fileManager = guestOperationsManager.fileManager;
result = false;
var attr = new VcGuestFileAttributes();
var srcFile = new File(vroPathToSetup);
var uri = fileManager.initiateFileTransferToGuest(vm , guestAuth ,guestPathToSetup, attr, srcFile.length, true);
result = fileManager.putFile(vroPathToSetup, uri);
 
var attr2 = new VcGuestFileAttributes();
var srcFile2 = new File(vroPathToVeeam);
var uri2 = fileManager.initiateFileTransferToGuest(vm , guestAuth ,guestPathToVeeam, attr2, srcFile2.length, true);
result = fileManager.putFile(vroPathToVeeam, uri2);

From here we move onto the “Execute setup.bat” scriptable task.  Again this script was borrowed and modified slightly from the “Run program in guest” workflow that is shipped with vRO –  the script and screencap of attribute/parameters are shown below

executesetup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var host = vm.sdkConnection;
 
var guestOperationsManager = host.guestOperationsManager;
var guestAuth = new VcNamePasswordAuthentication();
guestAuth.username = vmUsername;
guestAuth.password = vmPassword;
guestAuth.interactiveSession = false;
 
var guestProgramSpec = new VcGuestProgramSpec();
guestProgramSpec.programPath = guestPathToSetup;
guestProgramSpec.arguments = "";
guestProgramSpec.workingDirectory = "";
 
var processManager = guestOperationsManager.processManager;
result = processManager.startProgramInGuest(vm , guestAuth , guestProgramSpec);

And we are done…

updatecontextPretty simple right – once you have saved your workflow you can go ahead and execute it right away.  Or if you prefer, map the workflow within the vSphere Web Client to your VM inventory object – allowing you to simply right-click a Veeam B&R server and execute the script all without having to leave the web client.  Either way you are left with a quick, easy, and consistent way to upgrade all of your B&R servers without ever having to log into them – achievement unlocked

Keep in mind

  • You can only use this for virtualized Veeam servers – any physical servers could be automated, but you may need to chose another tool to do the copying and executing of the files
  • You need to ensure that no jobs are running when you perform the upgrade – This is something I’d love to build into the workflow but just need time (story of my life) – for now, manually cancel any currently running Veeam jobs before executing the workflow
  • The workflow reports success right away, before the upgrade is complete – again, I need time for this one.  For now, you can monitor the patch.log file that setup.bat creates – it should say something along the lines of exit code 0 returned when the upgrade as completed…

Happy update day!

Last weekend to shape your VMworld experience!

voteforpedroEvery year VMware takes the thousands of session abstracts submitted by community members and puts them out there to the masses so YOU can have your say, whether it be a yay or nay!  This year is no different so if you haven’t already cast your votes for the session you want to see you’d better hurry as voting ends this weekend, Sunday, May 18th @ 11:59 PDT.

Now there are a ton of great sessions available and by no means am I going to tell you which ones to vote on (wait, yes I am and I’m going to do it right now!)  If you are having trouble wading through them all or just want to get that clicking finger warmed up why  not just search for Session 1683 and give that one a vote.

session1683

For real though I have some high hopes for this session – I’ve teamed up with a couple of the brightest and smartest guys I know when it comes to vCO (James Bowling [blog/twitter] and Joerg Lew[blog/twitter]) to bring an informative panel session revolving around best practices, pitfalls, tips and tricks when it comes to vCenter Orchestrator.  We are hoping to turn this session into a real discussion with a lot of interaction from the audience.  So, if that sounds appealing to you then go ahead and give us the ‘Thumbs Up’.  If not, then leave unchecked – actually, don’t, just Thumbs up it anyway 🙂

Either way, VMworld 2014 is quickly approaching and I hope to see you there!

To The Point – Removing stranded vCenter Orchestrator servers from vCenter

Ever have an issue that no matter what you do you can’t seem to get rid of an old vCenter Orchestrator server that’s registered with vCenter?  No?  Well, you might want to just stop reading then!  Either way, I have and no matter how many times I un-configured the vCenter plug-in, removed the vCenter plug-in, reloaded the vCenter plug-in the entry would just not seem to go away.  After a slew of curse words and a half dozen restarts of vCO and SSO I finally prevailed – and this is how I did so.

Take the “mob” approach and “dispose” of the vCO server.

Don’t worry, there’s no illegal activity here – I’m not talking about taking Soprano action but what we will do is get a little Al Capone on the vSphere MOB (Managed Object Browser).  The MOB is basically a graphical representation or hierarchy of our vSphere objects which allows us to view properties and  invoke and perform different methods on different objects to achieve the respective results.  That’s about the best way I can explain it – anyways, let’s cut to the point and get rid of that registered vCO server.

Access the mob by opening up your favorite browser and navigating to https://IP_OF_VCENTER/mob/

Once authenticated let’s click our way to our vCenter extensions by hitting ‘content’, then ‘Extension Manager’

mob-content mob-extensionmanager

Here we can see all of the extensions that are currently registered with our vCenter Server, including our vCenter Orchestrator extension.

mob-vcolist

Before we can go ahead and unregister this extension we need to obtain the extension key or unique identifier.  To do so, you guessed it, click on it

mob-vcoextension key

If you have more than one vCO extension and can’t figure out which one is which try drilling further down into the object by clicking ‘server’ in order to get the URL associated with the object.  That should help you figure out which object key you need.

Copy that object key and head back to the ‘Extension List’ screen.  Here, we need to invoke a method located in the bottom section called ‘Unregister Extension’.    Simply paste in your extension key you obtained above and click ‘Invoke Method’.

 

mob-unregistermethodBooyah!  All should be well in the world again!  Hope someone finds this short, but to the point post helpful!  As always, comments, concerns and threats are all welcome through the outlets provided in the comments section.  Happy Friday and #GOHABSGO.

 

Quick To The Point – No port options on vCO ssh plug-in.

Do you run ssh on its standard and default port?  I for one, do not. That's why I was surprised when I found that the built in workflows for the ssh plugin in vCO have no input parameter for port and simply ride along the standard ssh port of 22

Thankfully for those of us that modify our sshd_config the underlying methods and constructs of the ssh plugin allow for us to specify a port, it's just the workflows built around them don't reflect that. So lets go through an example of modifying the "Run SSH Command" workflow to accept a port.

First up, duplicate the workflow. You don't want to modify the original as you have no idea what might be using it.

Next, we need to add an input parameter (type string) called, you guessed it. – port.

ttp-ssh-1

On the Presentation tab let's drag our port parameter up to the Host selection display group so things look nice and pretty

ttp-ssh-2

Now let’s edit the first scriptable task in the schema (Execute SSH Command).  We will need to ensure that we have access to our port parameter by mapping it in our ‘In’ tab.

ttp-ssh-3

And on the Scripting tab of that same element, we can see our session gets established on Line 2.  Here we can simply change the options we send to that construct from “var session = new SSHSession(hostName,username);” to “var session = new SSHSession(hostName,username,port);”

Save and close your workflow. Go ahead and test it now. You should now be executing your ssh commands on whichever port you have specified.  Now you can go ahead and add a port parameter to any workflow that references new SSHSession();

Migrating workflows from one vCO server to another!

Although vCenter Orchestrator does offer an upgrade path for their appliances there are times where I have found myself needing to deploy a new one and migrate all of my configuration and workflows to it.  vCO has some great built in workflows that can configure itself, but nothing that really deals with workflows.  Sure, you can export and import workflows one at time using the client, which may be ok if you have 10 workflows, but what if you have 20, 40, 100 that you need to migrate.  That could get pretty monotonous.

The shear energy of a mouse click.

That’s right – who wants to exert all that energy of clicking on the mouse to get these workflows migrated when we can just orchestrate or automate it – after all, this is vCenter Orchestrator we are talking about.  vCO has a REST plugin that allows us to create workflows around any application that offers one, but did you know that vCO also has it’s own REST API available for us to use?  So that’s where I started with my task of migrating workflows and by the time I got to writing this post it truly ended up being a community effort.

“Steal from the best”

This was a quote that I seen on one of Alan Renouf’s slides during a VMworld presentation on PowerCLI.  “Steal from the best”, “Don’t re-invent the wheel” – sayings that have resonated with me for my entire career – Why re-do something if it has already been done.  So when I set out on this small project I ended up using two main sources; This post by William Lam on VirtuallyGhetto on how to use curl to export a single vCO workflow and this forum reply by igaydajiev who “showed me the light” on how to import a workflow back in!  Without these two guys I wouldn’t have been able to do any of this.

Enough already let’s get to it!

So I chose to go the PowerShell route to accomplish this as I’m not too familiar with the REST plugin for vCO.  As well, I am targeting only a certain category – so basically what the script does is take in the following parameters

  • OldvCOServer – the old vCO appliance
  • OldvCOUser/OldvCOPass – credentials for the old appliance
  • OldvCOCategory – Category Name to export workflows from
  • TempFolder – Location to store the exported workflows
  • NewvCOServer – The new vCO appliance
  • NewvCOUser/NewvCOPass – credentials for the new appliance
  • NewvCOCategory – Category name on new server where you would like the worfkflows imported.

As far as an explanation I’ll just let you follow the code and figure it out.  It’s basically broke into two different sections; the export and the import.  During the import routine there is a little bit of crazy wonky code that gets the ID of the targeted category.  This is the only way I could figure out how to get it and I’m sure there is a way more efficient way of doing so, but for now, this will have to do.  Anyways, the script is shown below and is downloadable here.  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
Param(
  [string]$oldvCOServer="localhost",
  [string]$oldvCOUser="vcoadmin",
  [string]$oldvCOPass="vcoadmin",
  [string]$oldvCOCategory="MyOldWorkflows",
  [string]$newvCOServer="localhost",
  [string]$newvCOUser="vcoadmin",
  [string]$newvCOPass="vcoadmin",
  [string]$newvCOCategory="MyWorkflows",
  [string]$TempFolder
)
 
# vCO Port
$vcoPort="8281"
 
# Type to handle self-signed certificates
add-type @"
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    public class TrustAllCertsPolicy : ICertificatePolicy {
        public bool CheckValidationResult(
            ServicePoint srvPoint, X509Certificate certificate,
            WebRequest request, int certificateProblem) {
            return true;
        }
    }
"@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
[byte[]]$CRLF = 13, 10
 
function Get-AsciiBytes([String] $str) {
    return [System.Text.Encoding]::ASCII.GetBytes($str)            
}
 
function ConvertTo-Base64($string) {
   $bytes  = [System.Text.Encoding]::UTF8.GetBytes($string);
   $encoded = [System.Convert]::ToBase64String($bytes); 
 
   return $encoded;
}
 
clear
##############  EXPORT OLD WORKFLOWS ##############################
Write-Host "Beginning Export Routine" -ForegroundColor Black -BackgroundColor Yellow
Write-Host ""
# build uri
$uri = "https://"+$oldvCOServer+":"+$vCOPort+"/vco/api/workflows/?conditions=categoryName="+$oldvCOCategory
 
# Authentication token or old server
$token = ConvertTo-Base64("$($oldvCOUser):$($oldvCOPass)");
$auth = "Basic $($token)";
$header = @{"Authorization"= $auth; };   
 
#execute API call
$workflows = Invoke-RestMethod -URi $uri -Method Get -ContentType "application/xml" -Headers $header
Write-Host "Exporting $($workflows.total) workflows from $oldvCOCategory"
Write-Host "-------------------------------------------------------------------------------------------"
#loop through each workflow and export to TempFolder
foreach ($href in $workflows.link.href)
{
    #retrieve information about the specific workflow
    $header = @{"Authorization"= $auth; };   
    $workflow = Invoke-RestMethod -URi $href -Method Get -ContentType "application/xml" -Headers $header
 
    #replace all spaces in workflow name
    $workflowname = [System.Text.RegularExpressions.Regex]::Replace($($workflow.name),"[^1-9a-zA-Z_]","")
    $filename = $TempFolder + $workflowname + ".workflow"
    # setup new header
    $header = @{"Authorization"= $auth;
                "Accept"="application/zip"; }; 
    Write-Host "Exporting $($workflow.name) to $filename - " -NoNewline
    Invoke-RestMethod -URi $href -Method Get -ContentType "application/xml" -Headers $header -OutFile $filename
    Write-Host "Done" -ForegroundColor Green  
}
Write-Host ""
Write-Host "Export Routine Complete" -ForegroundColor Black -BackgroundColor Yellow
##################################################################
 
##############  IMPORT WORKFLOWS ##############################
Write-Host ""
Write-Host ""
Write-Host "Import Routines to new server" -ForegroundColor Black -BackgroundColor Yellow
Write-Host ""
 
#Generate auth for new vCO Server
$token = ConvertTo-Base64("$($newvCOUser):$($newVCOPass)");
$auth = "Basic $($token)";
 
#Get Category ID
$header = @{"Authorization"= $auth; };   
$uri = "https://"+$newvCOServer+":"+$vCOPort+"/vco/api/categories/"
$categories = Invoke-RestMethod -URi $uri -Method Get -Headers $header -ContentType "application/xml" 
foreach ($att in $categories.link)
{
    if ($att.attributes.value -eq "HPEDSB")
    {
        foreach ($newatt in $att.attributes )
        {
            if ($newatt.name -eq "id")
            {
                $categoryID = $newatt.value
            }
        }
    }
}
 
$impUrl = "https://$($newvCOServer):$($vcoPort)/vco/api/workflows?categoryId=$($categoryId)&overwrite=true";
$header = @{"Authorization"= $auth;
            "Accept"= "application/zip";
            "Accept-Encoding"= "gzip,deflate,sdch";};    
 
$workflows = Get-ChildItem $TempFolder -Filter *.workflow
Write-Host "Importing $($workflows.count) workflows to $newvCOCategory"
Write-Host "-------------------------------------------------------------------------------------------"
foreach ($workflow in $workflows)
{
    Write-Host "Importing $($workflow.name) - " -NoNewline
    $body = New-Object System.IO.MemoryStream
    $boundary = [Guid]::NewGuid().ToString().Replace('-','')
    $ContentType = 'multipart/form-data; boundary=' + $boundary
    $b2 = Get-AsciiBytes ('--' + $boundary)
    $body.Write($b2, 0, $b2.Length)
    $body.Write($CRLF, 0, $CRLF.Length)           
    $b = (Get-AsciiBytes ('Content-Disposition: form-data; name="categoryId"'))
    $body.Write($b, 0, $b.Length)
    $body.Write($CRLF, 0, $CRLF.Length)
    $body.Write($CRLF, 0, $CRLF.Length)
    $b = (Get-AsciiBytes $categoryId)
    $body.Write($b, 0, $b.Length)
    $body.Write($CRLF, 0, $CRLF.Length)
    $body.Write($b2, 0, $b2.Length)
    $body.Write($CRLF, 0, $CRLF.Length)     
    $b = (Get-AsciiBytes ('Content-Disposition: form-data; name="file"; filename="$($workflow.Name)";'))
    $body.Write($b, 0, $b.Length)
    $body.Write($CRLF, 0, $CRLF.Length)            
    $b = (Get-AsciiBytes 'Content-Type:application/octet-stream')
    $body.Write($b, 0, $b.Length)
    $body.Write($CRLF, 0, $CRLF.Length)
    $body.Write($CRLF, 0, $CRLF.Length)
    $b = [System.IO.File]::ReadAllBytes($workflow.FullName)
    $body.Write($b, 0, $b.Length)
    $body.Write($CRLF, 0, $CRLF.Length)
    $body.Write($b2, 0, $b2.Length)
    $b = (Get-AsciiBytes '--');
    $body.Write($b, 0, $b.Length);
    $body.Write($CRLF, 0, $CRLF.Length);
 
    $header = @{"Authorization"= $auth;
            "Accept"= "application/zip";
            "Accept-Encoding"= "gzip,deflate,sdch";};    
    Invoke-RestMethod -Method POST -Uri $impUrl -ContentType $ContentType -Body $body.ToArray() -Headers $header
    Write-Host "Done" -foregroundcolor Green
}
 
Write-Host ""
Write-Host "Import Routine complete" -ForegroundColor Black -BackgroundColor Yellow
##################################################################