Adding a generic service to cluster from powershell - powershell

I'm a newbie in clustering and I'm trying to create a generic service to a cluster using PowerShell. I can add it without any issues using the GUI, but for some reason I cannot add it from PowerShell.
Following the first example from the documentation for Add-ClusterGenericServiceRole, I've tried the following command:
Add-ClusterGenericServiceRole -ServiceName "MyService"
This throws the following error:
Static network was [network range] was not configured. Please use -StaticAddress to use this network or -IgnoreNetwork to ignore it.
What's the connection between the network and my service? And why aren't these details required when creating it from the GUI?
I also tried another approach, creating the resource with:
Add-ClusterResrouce -Name MyService -ResourceType "Generic Serice"
This command succeeded but I noticed in the GUI that the ServiceName is blank, and thus the actual service cannot be started. If I could somehow change the ServiceName property it should do the trick. Again, from PowerShell I tried the following:
$resource = Get-ClusterResrouce "MyService"
$Resource.ServiceName = "Actual name of service" //property ServiceName cannot be found on this object.
I've been struggling for a couple of hours now with no luck. Is there something basic I'm missing? I think this shouldn't be as complicated as it might look.

I had the same problem; I had to add a large amount of services and got stuck with the "ServiceName" as well.
First, a note on the Add-ClusterGenericServiceRole command: this is for creating the service resource and the role at the same time, as opposed to just adding the service resource to an existing role.
Now, the solution is that you have to set the parameter "ServiceName" with the Set-ClusterParameter command. You can do this for an existing service resource like this:
Get-ClusterResource "ServiceDisplayName" | Set-ClusterParameter -Name ServiceName -Value "ServiceName"
However, you probably want to create the resource with everything it needs in one go, like this:
Add-ClusterResource -Name "ServiceDisplayName" -Group "cluster role" -ResourceType "Generic Service" | Set-
ClusterParameter -Name ServiceName -Value "ServiceName"

Related

How do I update the IP whitelist for a staging slot via Azure Powershell from an Azure DevOps Release Pipeline?

I have an application hosted in Azure, and I use Azure DevOps to manage my build and release pipelines. As part of the release, I warm up the application by making a request to the root url (e.g. https://myapp.azurewebsites.net). In order to make this request I must first make sure the hosted build agent running the deployment has access to that url (or I will get a 403). I have written a short powershell script to achieve this, and put it in an Azure Powershell task. It adds the IP of the build agent to the IpSecurityConfiguration of the app service. So far so good. It works perfectly for apps that are just apps. Where it falls down is when I try to use it against a staging environment. When we release to production we first push the code to a staging slot, then flip it over to live when we've run our tests and made sure everything is good. The powershell script that correctly handles the IpSecurityConfiguration for the app services does not work on the staging slot. To access a staging slot, we use myappname/slots/staging for the variable $(WebApiName), normally it would just be the name of the app service itself. Again, this works perfectly if I run the script from my local environment, it only fails in the pipeline. The code is below:
# Whitelist Azure Agent IPs
$agentIP = Invoke-RestMethod http://ipinfo.io/json | Select -exp ip
Write-Host "Connecting to Azure"
$APIVersion = ((Get-AzureRmResourceProvider -ProviderNamespace Microsoft.Web).ResourceTypes | Where-Object ResourceTypeName -eq sites).ApiVersions[0]
Write-Host "API Version is $APIVersion. Getting web app config for $(WebApiName) in $(ResourceGroupName)"
$WebApiConfig = (Get-AzureRmResource -ResourceType Microsoft.Web/sites/config -ResourceName $(WebApiName) -ResourceGroupName $(ResourceGroupName) -ApiVersion $APIVersion)
Write-Host "Got web app config: $WebApiConfig"
$webIP = [PSCustomObject]#{
ipAddress = "$agentIP/32";
action = "Allow";
tag = 'Default';
priority = 300;
name = $agentIP.ToString();
description = $agentIP.ToString()
}
Write-Host "Adding $agentIP to security restrictions"
$WebApiConfig.Properties.ipSecurityRestrictions += $webIP
Write-Host "Updating security restrictions"
# update app restrictions, do not prompt for confirmation
$result = Set-AzureRmResource -ResourceId $WebApiConfig.ResourceId -Properties $WebApiConfig.Properties -ApiVersion $APIVersion -Force
To muddy the water somewhat, I can get the exact same code to work perfectly with the staging slot locally by changing
$WebApiConfig = (Get-AzureRmResource -ResourceType Microsoft.Web/sites/config -ResourceName $(WebApiName) -ResourceGroupName $(ResourceGroupName) -ApiVersion $APIVersion)
to
$WebApiConfig = (Get-AzureRmResource -ResourceType Microsoft.Web/sites -ResourceName $(WebApiName)/config -ResourceGroupName $(ResourceGroupName) -ApiVersion $APIVersion)
but this doesn't work in the Azure Powershell task. Instead I can't deploy to any environment because the task fails while trying to access IpSecurityRestrictions on the $WebApiConfig object. The exception is "Exception setting "ipSecurityRestrictions": "The property 'ipSecurityRestrictions' cannot be found on this object. Verify that the property exists and can be set."
As I said earlier, if I run the script in exactly this form locally, it works perfectly. Obviously I have to manually replace the variables that come from the build pipeline, but otherwise there is no difference between code that works exactly as I want it to on my local machine and code that fails in the release. You can verify this by swapping out $(WebApiName) for a valid app service name and $(ResourceGroupName) for the resource group that app service is in. I put a line in about halfway down that outputs $WebApiConfig so that I can see what it is, and on my local machine I see a valid object, while in the output of the task I get nothing. The line just says "Got web app config:"
Anyone got any ideas?
I've tried changing the version of powershell used by the task to
match the version I've got.
I've tried using the preview version of the task (v4, otherwise I've been using v3).
I've tried every permutation of /sites/config everywhere I can think of in the call to Get-AzureRmResource (since that was what allowed it to work locally on the slot).
Just one final thing in case anyone wonders. I'm doing it this way instead of whitelisting all the IPs in Microsoft's list (https://www.microsoft.com/en-us/download/confirmation.aspx?id=41653) for two reasons, firstly it's a lot easier to maintain a short list of our own IPs, and secondly there seems to be a bug somewhere in the way Azure handles those CIDR definitions because IPs that are categorically in those ranges are frequently blocked during our deployments even when we have the entire file whitelisted. This way I just add whichever IP is currently being used dynamically to the whitelist, and remove it after we're done. Assuming I can get it to work...
Finally figured out the solution to this. In order to work with slots the resource type has to be subtly different. This line works in an Azure Powershell task:
$WebApiConfig = (Get-AzureRmResource -ResourceType Microsoft.Web/sites/slots/config -ResourceName $(WebApiName) -ResourceGroupName $(ResourceGroupName) -ApiVersion $APIVersion)
Posting in case it helps anyone else with the same issue. I can confirm that the approach I've taken works great in managing access to Azure sites by build agents, and saves a lot of messing around with Microsoft's build agent xml file.

Get-AzureRmResource returns different data depending on how it was called

This just blew my mind. I run these commands to access a particular resource and print out its location:
PS H:\> $hmm = Get-AzureRmResource -ResourceGroupName "RG_NAME" -ResourceName "R_NAME" -ResourceType "Microsoft.ServiceBus/namespaces"
PS H:\> $hmm.Location
East US 2
But if I run these commands I get different data for the same field:
PS H:\> $hmm2 = Get-AzureRmResource | Where-Object {$_.ResourceName -match "R_NAME"}
PS H:\> $hmm2.Location
eastus2
Before you ask, I only have one resource whose name is "R_NAME".
Why is the Azure API returning different values depending on how I try to access the data? Is there some kind of conversion happening in the background on Azure that's normalizing the data or something?
No one except for the devs will be able to answer this question (why this happens exactly). But probably this happens because when you do a get against the subscription you are talking to an Azure Resource provider and when talking to a single resource you are talking to a servicebus provider. And their responses differ. This can happen. Microsoft is a huge company. things like this happen all the time.

Why would Get-Service not find the service with powershell

I am having problems with a powershell script.
I wrote a script that would search for a windows service with a specific name, and it would Stop or Start this service.
It works when I run it on a server which I log into with a service account that I know that can access the service console. However when it runs off of my build server, the script is no longer able to find the services. I tried giving the service account that runs script the same privaledges as the other service account but that doesn't seem to work.
[System.ServiceProcess.ServiceController]$service = Get-Service -Name $ServiceName -ComputerName $Remoteserver -ErrorAction SilentlyContinue
That is the line that is not longer able to find the service. What am I doing wrong. Is there a way to impersonate a user that can find the service? Any help would be appreciated.
You could try supplying the credentials of the service account using the -Credential parameter. However, since you imply that it used to work with the account that runs the script remotely and no longer does, I think a more likely culprit is that $ServiceName used to only match one service on the target computer, and now there is another service whose name matches that string. If more than one service matches the -Name parameter, Get-Service returns an array of ServiceController objects.
Try running it without ErrorAction -SilentlyContinue. If you get the following error message, then that's what's happening:
Cannot convert the "System.Object[]" value of type "System.Object[]" to type "System.ServiceProcess.ServiceController".
If you get a different error message, please add the full error message to the question.

Azure ReservedIP endpoints

I'm trying to provision a new Azure VM from an image via PowerShell and I need to use ReservedIP (which is why I'm using PowerShell - ReservedIP functionality isn't available from the management portal).
I'm running the following command:
New-AzureVMConfig -Name "myName" -InstanceSize Small -ImageName "imageName" | New-AzureVM -ServiceName "serviceName" -ReservedIPName "IP Name" -AffinityGroup "myAffinityGroup"
But I get the following error:
New-AzureVM : BadRequest: Deployment serviceName uses ReservedIP IP Name but does not contain any endpoints. Deployment must contain at least one endpoint in order to use a ReservedIP.
I can see that the cloud instance gets created, but it doesn't have any VM instance in it. Other Stack Overflow posts seem to imply that the above pshell commands should just work. None of the documentation addresses the need to add an endpoint and the VM doesn't even get created, so I don't know where I'd be able to add one.
Any help would be much appreciated. Thanks!
Figured it out!
You need to add an Add-AzureEndpoint call after New-AzureVMConfig instead of after New-AzureVM.
I wanted to move a machine to a cloud service with a static ip, so I deleted the VM (keeping the disk), then once the disk showed up for use I ran the code below. I know it will have a bogus endpoint that I will have to remove and re-create later. I just wanted it to be created.
New-AzureVMConfig -Name "test" -InstanceSize Large -DiskName "test-test-0-201409031948580187" |Add-AzureEndpoint -Name "test" -Protocol "tcp" -PublicPort 80 -LocalPort 80 -LBSetName "test" -ProbePort 888 -ProbeProtocol "TCP"| New-AzureVM -ServiceName "test" –ReservedIPName "SQL-UAT-USEast" -VNetName "East-1" -Location "East US"

Azure Powershell Get Public IP of Staging Cloud Service

I am using this answer to look up our cloud service's public ip.
Azure Powershell: Get public virtual IP of service
This only returns the endpoints for the production cloud services. We also need to easily look up our staging cloud service endpoints.
Does anyone know how to look these up with Azure Powershell?
Thanks!
Get-AzureDeployment and System.Net.Dns class will help.
Get-AzureDeployment -ServiceName your_service_name -Slot Staging
You will get lots of properties, including URL
Then you can run following PS command which will give you IP address (where your_url is usually XX.cloudapp.net) for the given domain name:
[System.Net.Dns]::GetHostAddresses("your_url") | foreach {echo $_.IPAddressToString }
I hope that will help.
This is one-liner and uses nothing other then Get-AzureDeployment - and returs directly the VIP, as long as you have at least one InputEndpoint defined - otherwise you can't connect to the service anyway ;) No need of heavy lifting or using DNS client ...
PS C:\> Get-AzureRole -ServiceName "your_service_name" -Slot "staging" -InstanceDetails -RoleName "your_desired_role_name_when_you_have_more_than_one_role" | select {$_.InstanceEndpoints[0].VIP }
And you will get the VIP. The important option here is -InstanceDetails which will get the Endpoints.
Here is the simplest answer I could think of. It returns a string containing the VIP of the service in the slot you specify.
PS C:\> (Get-AzureDeployment -ServiceName 'ServiceName' -Slot Staging).VirtualIPs.Address