I have a Windows Azure cloud service with a Web Role and a Worker Role. I I have built a website that allows me to perform various management functions against the cloud service (stop/start, reboot instance, add instance, remove instance). All functions are performed via the web api. My issue is that when I add an instance of the web role, the worker role reboots. Note that this doesn't happen if I add an instance via the Azure portal. The code functions correctly in all other aspects.
Any idea how to do this so that only the role being affected recycles instead of all roles recycling?
My code:
public void AddInstance()
{
XDocument configDoc = this.GetDeploymentConfiguration();
var ns = configDoc.Root.GetDefaultNamespace();
configDoc.Root
.Elements( ns + "Role" )
.FirstOrDefault( r => r.Attribute( "name" ).Value.ToLower() == this.RoleName.ToLower() )
.Element( ns + "Instances" )
.Attribute( "count" )
.Value = ( int.Parse( configDoc.Root
.Elements( ns + "Role" )
.FirstOrDefault( r => r.Attribute( "name" ).Value.ToLower() == this.RoleName.ToLower() )
.Element( ns + "Instances" )
.Attribute( "count" )
.Value ) + 1 ).ToString();
string encodedString = Convert.ToBase64String( Encoding.UTF8.GetBytes( configDoc.ToString() ) );
this.SetDeploymentConfig( encodedString );
}
public XDocument GetDeploymentConfiguration()
{
string uri = string.Format( this.servicePropertiesOperationFormat, this.subscriptionID, this.serviceName, "production", "" );
ServiceManagementOperation operation = new ServiceManagementOperation( this.thumbprint, this.versionID );
var xdoc= operation.Invoke( uri );
var myelm = xdoc.Element( wa + "Deployment" ).Element( wa + "Configuration" );
var mystring= Encoding.UTF8.GetString( Convert.FromBase64String( myelm.Value ) );
return XDocument.Parse( mystring );
}
public string SetDeploymentConfig( string configurationFile )
{
string uri = string.Format( this.servicePropertiesOperationFormat, this.subscriptionID, this.serviceName, "production", "/?comp=config" );
ServiceManagementOperation operation = new ServiceManagementOperation( this.thumbprint, this.versionID );
string payloadString = string.Format(
#"<?xml version=""1.0"" encoding=""utf-8""?>
<ChangeConfiguration xmlns=""http://schemas.microsoft.com/windowsazure"">
<Configuration>{0}</Configuration>
</ChangeConfiguration>", configurationFile );
XDocument payload = XDocument.Parse( payloadString );
return operation.Invoke( uri, payload );
}
It's not very intuitive, but you have to cancel the scaling event, otherwise this will tell Azure to reboot other instances. Add the following line to the OnStart Method in Your RoleEntryPoint file:
RoleEnvironment.Changing += (sender, args) => { args.Cancel = false; };
Related
I am working on stress testing for our IoT Usecase.
For testing, I need to create 100 devices.
So, I have developed one azure function to use IoTHubs Import Device feature as per MSFT docs.
When I used, sample code using Public Storage account for Import/Output blob container SAS token.
It worked as per expectation and created devices on IoTHub.
But when I am using same code with Private Storage account, it is sometimes throwing reading error and sometimes it is throwing writing error on blob storage, even if SAS token has all required permissions (Read, Write, Delete, Create, List, Add etc..), and even private storage account is also having DNS configuration available. And for other use, I am able to add/update/delete blobs on the same private storage account.
Only problem I am facing is while calling ImportDevicesAsync method of IoTHub's RegistryManager.
My sample code is as below:
To create devices.txt file and upload it on proper container.
for (int i = deviceIndex; i < deviceCount + deviceIndex; i++)
{
var deviceToAdd = new ExportImportDevice()
{
Id = $"{devicePrefix}{i.ToString().PadLeft(6, '0')}",
ImportMode = importMode == "delete" ? ImportMode.Delete : ImportMode.Create,
Status = DeviceStatus.Enabled,
Authentication = new AuthenticationMechanism()
{
SymmetricKey = new SymmetricKey()
{
PrimaryKey = CryptoKeyGenerator.GenerateKey(32),
SecondaryKey = CryptoKeyGenerator.GenerateKey(32)
}
},
Tags = new TwinCollection(initialTags.SerializeObject())
};
serializedDevices.Add(deviceToAdd.SerializeObject());
}
// Write the list to the blob
StringBuilder sb = new();
serializedDevices.ForEach(serializedDevice => sb.AppendLine(serializedDevice));
Uri uri = new(assetsBlockBlobUrl + "?" + assetsBlobContainerSas);
CloudBlockBlob blob = new(uri);
await blob.DeleteIfExistsAsync();
using (CloudBlobStream stream = await blob.OpenWriteAsync())
{
byte[] bytes = Encoding.UTF8.GetBytes(sb.ToString());
for (var i = 0; i < bytes.Length; i += 500)
{
int length = Math.Min(bytes.Length - i, 500);
await stream.WriteAsync(bytes.AsMemory(i, length));
}
}
To import devices from the same container using registryManager.ImportDeviceAsync method:
RegistryManager registryManager = RegistryManager.CreateFromConnectionString(Environment.GetEnvironmentVariable("iotHubConnectionString"));
JobProperties importJob = await registryManager.ImportDevicesAsync(containerSasUri, containerSasUri);
////Wait until job is finished
while (true)
{
importJob = await registryManager.GetJobAsync(importJob.JobId);
_logger.LogInformation("import job " + importJob.Status);
if (importJob.Status == JobStatus.Completed)
{
return Common.Utils.GetObjectResult(importMode == "delete" ? MessageConstants.SuccessDeletedAsset : MessageConstants.SuccessCreatedAsset);
}
else if (importJob.Status == JobStatus.Failed)
{
return Common.Utils.GetObjectResult(importMode == "delete" ? MessageConstants.DeleteDeviceFail : MessageConstants.CreateDeviceFail);
}
else if (importJob.Status == JobStatus.Cancelled)
{
return Common.Utils.GetObjectResult(importMode == "delete" ? MessageConstants.DeviceDeletionCancel : MessageConstants.DeviceCreationCancel);
}
await Task.Delay(TimeSpan.FromSeconds(5));
}
Trying to create a POC for azure signalr service. I found the github samples, which appeared to have some solid examples. I chose this one. Basically, my problem is that when I run the code locally, works like a champ with the localhost url, but when I try to run using an Azure SignalR Service using a url that I copied from azure portal keys which is in this format: Endpoint=<service_endpoint>;AccessKey=<access_key>;, I get an error stating that "Invalid URI: The URI scheme is not valid.". How do I transform the url from what I copy from keys and use it to connect to a signalr service?
class Program
{
private const string DefaultHubEndpoint = "Endpoint=http://someFakesrsname.service.signlar.net;AccsssKey=thisseemslikeagoodaccesskeytouseformyquestion";//"http://localhost:5000/ManagementSampleHub";
private const string Target = "Target";
private const string DefaultUser = "User";
static void Main(string[] args)
{
var app = new CommandLineApplication();
app.FullName = "Azure SignalR Management Sample: SignalR Client Tool";
app.HelpOption("--help");
var hubEndpointOption = app.Option("-h|--hubEndpoint", $"Set hub endpoint. Default value: {DefaultHubEndpoint}", CommandOptionType.SingleValue, true);
var userIdOption = app.Option("-u|--userIdList", "Set user ID list", CommandOptionType.MultipleValue, true);
app.OnExecute(async () =>
{
var hubEndpoint = hubEndpointOption.Value() ?? DefaultHubEndpoint;
var userIds = userIdOption.Values != null && userIdOption.Values.Count > 0 ? userIdOption.Values : new List<string>() { "User" };
Console.WriteLine("hubEndpoint: " + hubEndpoint);
Console.WriteLine("DefaultHubEndpoint: " + DefaultHubEndpoint);
foreach (var userId in userIds)
{
Console.WriteLine("UserId: " + userId);
}
var connections = (from userId in userIds
select CreateHubConnection(hubEndpoint, userId)).ToList();
await Task.WhenAll(from conn in connections
select conn.StartAsync());
Console.WriteLine($"{connections.Count} Client(s) started...");
Console.ReadLine();
await Task.WhenAll(from conn in connections
select conn.StopAsync());
return 0;
});
app.Execute(args);
}
static HubConnection CreateHubConnection(string hubEndpoint, string userId)
{
var url = hubEndpoint.TrimEnd('/') + $"?user={userId}";
var connection = new HubConnectionBuilder().WithUrl(url).Build();
connection.On(Target, (string message) =>
{
Console.WriteLine($"{userId}: gets message from service: '{message}'");
});
connection.Closed += async ex =>
{
Console.WriteLine(ex);
Environment.Exit(1);
};
return connection;
}
}
enter code here
In my project I am supposed to get data from openweathermap.org and put that in a collection in my DocumentDB database in Azure.
The code below works locally on my development machine, but when i upload the project, it runs and succeed (says the dashboard) but no documents are created. I can only create the documents if I run from local machine.
Why is that?
Here is my code:
public static void Main()
{
JobHost host = new JobHost();
// The following code ensures that the WebJob will be running continuously
host.Call(typeof(Program).GetMethod("saveWeatherDataToAzureDocumentDB"));
}
[NoAutomaticTrigger]
public static async void saveWeatherDataToAzureDocumentDB()
{
string endpointUrl = ConfigurationManager.AppSettings["EndPointUrl"];
string authorizationKey = ConfigurationManager.AppSettings["AuthorizationKey"];
string url = "http://api.openweathermap.org/data/2.5/weather?q=hanstholm,dk&appid=44db6a862fba0b067b1930da0d769e98";
var request = WebRequest.Create(url);
string text;
var response = (HttpWebResponse)request.GetResponse();
using (var sr = new StreamReader(response.GetResponseStream()))
{
text = sr.ReadToEnd();
}
// Create a new instance of the DocumentClient
var client = new DocumentClient(new Uri(endpointUrl), authorizationKey);
// Check to verify a database with the id=FamilyRegistry does not exist
Database database = client.CreateDatabaseQuery().Where(db => db.Id == "weatherdata").AsEnumerable().FirstOrDefault();
// If the database does not exist, create a new database
if (database == null)
{
database = await client.CreateDatabaseAsync(
new Database
{
Id = "weatherdata"
});
}
// Check to verify a document collection with the id=FamilyCollection does not exist
DocumentCollection documentCollection = client.CreateDocumentCollectionQuery(database.SelfLink).Where(c => c.Id == "weathercollection").AsEnumerable().FirstOrDefault();
// If the document collection does not exist, create a new collection
if (documentCollection == null)
{
documentCollection = await client.CreateDocumentCollectionAsync("dbs/" + database.Id,
new DocumentCollection
{
Id = "weathercollection"
});
}
//Deserialiser til et dynamisk object
if (text == "")
{
mark m = new mark() { name = "Something" };
await client.CreateDocumentAsync(documentCollection.DocumentsLink, m);
}
else
{
var json = JsonConvert.DeserializeObject<dynamic>(text);
json["id"] = json["name"] + "_" + DateTime.Now;
await client.CreateDocumentAsync(documentCollection.DocumentsLink, json);
}
}
public sealed class mark
{
public string name { get; set; }
}
UPDATE - This is what I have in my App.config
<appSettings>
<!-- Replace the value with the value you copied from the Azure management portal -->
<add key="EndPointUrl" value="https://<My account>.documents.azure.com:443/"/>
<!-- Replace the value with the value you copied from the Azure management portal -->
<add key="AuthorizationKey" value="The secret code from Azure"/>
Also, At DocumentDB Account i find the Connection string like this. AccountEndpoint=https://knoerregaard.documents.azure.com:443/;AccountKey=my secret password
How should I apply this to the WebJob?
Appriciate your help!
I am trying to check if a user is an admin on a particular machine.
I have the following code that works fine when the computer is on the same domain:
public bool CheckAdmins(string computerName)
{
var identity = WindowsIdentity.GetCurrent();
var principal = new WindowsPrincipal(identity);
string branchnumber = computerName.Substring(0, 3);
bool admin = false;
if (logonUser.authenticate())
{
using (DirectoryEntry machine = new DirectoryEntry("WinNT://" + logonUser.Domain + "/" + computerName,logonUser.Domain + "\\" + logonUser.UserID,logonUser.Password))
{
//get local admin group
using (DirectoryEntry group = machine.Children.Find("Administrators","group"))
{
//get all members of local admin group
object members = group.Invoke("Members", null);
foreach (object member in (IEnumerable)members)
{
//get account name
string accountName = new DirectoryEntry(member).Name;
bool isAdmin = principal.IsInRole(accountName);
if (isAdmin == true) { admin = true; }
}
}
}
}
return admin;
}
However, across domain, this simply comes back with 'network path not found'.
I have been experimenting with LDAP but not getting too far. I have tried a number of methods and ideally need an example. This is what I am using currently:
String strPath = "LDAP://172.24.242.51/CN=258TP520,OU=258,DC=net,DC=test,DC=co,DC=uk";
DirectoryEntry myDE = new DirectoryEntry(strPath, "testdom\user", "password");
List<string> memberof = new List<string>();
foreach (object oMember in myDE.Properties["memberOf"])
{
memberof.Add(oMember.ToString());
}
However myDE.properties doesn't seem to contain anything. All help appreciated!
Thanks
I needed to append the FQDN to the computername, like so
using (DirectoryEntry machine = new DirectoryEntry("WinNT://" + computerName + ".net.test.co.uk",logonUser.Domain + "\\" + logonUser.UserID,logonUser.Password))
This fixed my issue.
I want to create a new application group in every team project and grant rights to it. What I've done so far is iterating over all projects and creating the group, if not already present:
static void Main( string[] args )
{
m_TfsServer = new TfsTeamProjectCollection(
new Uri( "http://server:port/vdir" ),
System.Net.CredentialCache.DefaultNetworkCredentials,
new UICredentialsProvider( ) );
m_TfsServer.EnsureAuthenticated( );
m_TfsSecurityService = m_TfsServer.GetService<IGroupSecurityService>( );
var structService = m_TfsServer.GetService<ICommonStructureService>( );
foreach ( var p in structService.ListAllProjects( ) )
{
string groupSid;
if ( !GroupExist( p.Uri, GroupName ) )
{
groupSid = m_TfsSecurityService.CreateApplicationGroup(
p.Uri,
GroupName,
GroupDescription );
}
else
{
groupSid = GetApplicationGroupSid( p.Uri, GroupName );
}
Identity userIdentity = m_TfsSecurityService.ReadIdentityFromSource(
SearchFactor.AccountName,
UserName );
if ( !m_TfsSecurityService.IsMember( groupSid, userIdentity.Sid ) )
{
m_TfsSecurityService.AddMemberToApplicationGroup(
groupSid,
userIdentity.Sid );
}
}
}
private static bool GroupExist( string projectUri, string groupName )
{
bool result = false;
Identity[] groups =
m_TfsSecurityService.ListApplicationGroups( projectUri );
foreach ( Identity group in groups )
{
result |= group.SecurityGroup && group.DisplayName.Equals( groupName );
}
return result;
}
private static string GetApplicationGroupSid(
string projectUri,
string groupName)
{
return m_TfsSecurityService.ListApplicationGroups( projectUri )
.Where( g => g.DisplayName.Equals( groupName ) )
.Select( g => g.Sid )
.First( );
}
The only thing left is to grant the "View project-level information" right to the group.
[Edit]
I found something to grant rights using the VersionControlService:
var tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection( ServerUri );
tfs.EnsureAuthenticated( );
var vcs = tfs.GetService<VersionControlServer>( );
//vcs.SetPermissions( new SecurityChange[] { } ); ???
But I did not find any documentation how to grant rights to a group, so I'm even not sure whether this solution is the correct approach.
[/Edit]
Has anyone experiences with the TFS rights management or anyone who already granted rights via TFS API?
Not exactly what you're asking for, but it may be similar enough to get you started.
http://blogs.microsoft.co.il/blogs/shair/archive/2009/02/03/tfs-api-part-12-set-security-for-area-iteration.aspx