I'm having a very hard time with what I feel should be a simple task. Every week, our team queries VMware vCenter for three pieces of output: VM counts in three different locations. Here is what it looks like:
Name Value
---- -----
locationA 1433
locationB 278
locationC 23
The information is emailed to our team, as well as some of the higher-ups who like to see the data. This is all automated with a Powershell script and Windows Task Scheduler running on a server, no problems.
That data is also placed in a Google sheet. We just append a new row with the date, and copy and paste the data into the three existing columns. It takes 30 seconds, once a week. Seems silly given how little time it takes to copy it over to the Google sheet but I really want to automate that last process using Google Sheets API.
I seem to keep finding and persuing what feel are online wild goose chases, in the Google scripting to accessing and editing Google sheets. I've downloaded and installed the Sheets API libraries, Drive API libraries, the Google .net library, set up the Google developer site, and run through the Google sheets API documentation and OAuth authenticating. I'm using Visual Studio 2013 because I figured that would play the best with Powershell and calling the .net commands.
I have pretty much no coding experience outside of Powershell (if you can call that coding). I can't even figure out how to pull the Google sheet, much less do anything to it. Nothing I've tried is working so far, and for what little time it takes to copy this info manually every week I've already spent so much more time than is probably worth it. I feel like if I can get a handle on this, that would open the door for further Google automation in the future since we operate with a Google domain. At any rate, help is very much appreciated.
Here is my latest scripting attempt in Visual Studio:
using System;
using Google.GData.Client;
using Google.GData.Spreadsheets;
namespace MySpreadsheetIntegration
{
class Program {
static void Main(string[] args)
{
string CLIENT_ID = "abunchofcharacters.apps.googleusercontent.com";
string CLIENT_SECRET = "secretnumber";
string REDIRECT_URI = "https://code.google.com/apis/console";
OAuth2Parameters parameters = new OAuth2Parameters();
parameters.ClientId = CLIENT_ID;
parameters.ClientSecret = CLIENT_SECRET;
parameters.RedirectUri = REDIRECT_URI;
parameters.Scope = SCOPE;
string authorizationUrl = OAuthUtil.CreateOAuth2AuthorizationUrl(parameters);
Console.WriteLine(https://code.google.com/apis/console);
Console.WriteLine("Please visit the URL above to authorize your OAuth "
+ "request token. Once that is complete, type in your access code to "
+ "continue..."));
parameters.AccessCode = Console.ReadLine();
OAuthUtil.GetAccessToken(parameters);
string accessToken = parameters.AccessToken;
Console.WriteLine("OAuth Access Token: " + accessToken);
GOAuth2RequestFactory requestFactory =
new GOAuth2RequestFactory(null, "MySpreadsheetIntegration-v1", parameters);
SpreadsheetsService service = new SpreadsheetsService("MySpreadsheetIntegration-v1");
service.RequestFactory = requestFactory;
var driveService = new DriveService(auth);
var file = new File();
file.Title = "VSI - VM Totals by Service TEST";
file.Description = string.Format("Created via {0} at {1}", ApplicationName, DateTime.Now.ToString());
file.MimeType = "application/vnd.google-apps.spreadsheet";
var request = driveService.Files.Insert(file);
var result = request.Fetch();
var spreadsheetLink = "https://docs.google.com/spreadsheets/d/GoogleDoc_ID";
Console.WriteLine("Created at " + spreadsheetLink);
End Class;
End Namespace;
}
}
}
For anyone still following this, I found a solution. I was going about this entirely the wrong way (or at least a way that I could comprehend). One solution to my issue was to create a new Google Script that only accessed my email once a week (after we got the report) and teased out everything but the data I was looking for and sent it to the Google spreadsheet.
Here's the script:
function SendtoSheet(){
var threads = GmailApp.search("from:THESENDER in:anywhere subject:THESUBJECTOFTHEEMAILWHICHNEVERCHANGES")[0];
var message = threads.getMessages().pop()
var bodytext = message.getBody();
counts = []
bodytext = bodytext.split('<br>');
for (var i=0 ; i < bodytext.length; i++){
line = bodytext[i].split(':');
if (line.length > 0){
if (!isNaN(line[1])){counts.push(line[1]);}}}
var now = new Date()
counts.unshift(Utilities.formatDate(now, 'EST', 'MM/dd/yyyy'))
var sheet = SpreadsheetApp.openById("GoogleDocID")
sheet = sheet.setActiveSheet(sheet.getSheetByName("Data by Week"))
sheet.appendRow(counts)
}
That Counts array contains the magic to extract the numeric data by breaking up by line breaks and :'s. Works perfectly. It didn't involve figuring out how to use Visual Studio, or the .net Google libraries, or editing the running PowerShell script. Clean and easy.
Hope this helps someone.
Related
I'm trying to use the dll version of ghost script in an azure web app. It has worked before, but now I start getting these errors when using Ghostscript.
An error occured when call to 'gsapi_new_instance' is made: -100
I'm using Ghostscript.NET for working with Ghostscript.
From reading other posts here, it could be because of missing permissions. But I'm not sure which permissions I'm missing.
According to your description, I have created a test demo using Ghostscript.NET with "gsdll32.dll" and published the application to azure. It works well(I don't change any setting).
As you said, it worked well before. I think this error is not related with your permissions. I suggest you could follow below step to troubleshoot the application.
1.Use remote debug to check the gsdll32.dll path is right in your application code
2.Create a new app web app and published the application to it and test again.
More details about my test codes, you could refer to below:
GhostscriptVersionInfo gvi = new GhostscriptVersionInfo(Server.MapPath("~/dll/gsdll32.dll"));
GhostscriptProcessor proc = new GhostscriptProcessor(gvi);
string inputFile = Server.MapPath("~/File/Test Document.pdf");
string outputFile = Server.MapPath("~/File/page-%03d.png");
int pageFrom = 1;
int pageTo = 2;
List<string> switches = new List<string>();
switches.Add("-empty");
switches.Add("-dSAFER");
switches.Add("-dBATCH");
switches.Add("-dNOPAUSE");
switches.Add("-dNOPROMPT");
switches.Add("-dFirstPage=" + pageFrom.ToString());
switches.Add("-dLastPage=" + pageTo.ToString());
switches.Add("-sDEVICE=png16m");
switches.Add("-r96");
switches.Add("-dTextAlphaBits=4");
switches.Add("-dGraphicsAlphaBits=4");
switches.Add(#"-sOutputFile=" + outputFile);
switches.Add(#"-f");
switches.Add(inputFile);
proc.Process(switches.ToArray());
Result:
Our organization utilizes VisualStudioOnline, GitHub and BitBucket for various repositories. I've been trying to figure out a way in c# to automate the pull of changes from various git repositories on a regular schedule. I've tried starting a process like this and then redirecting the standard input/output.
var p = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = #"C:\Program Files (x86)\Git\bin\sh.exe",
RedirectStandardInput = true,
UseShellExecute = false,
RedirectStandardOutput = true,
Arguments = "--login -i"
}
};
p.Start();
using (var so = p.StandardOutput)
{
using (var si = p.StandardInput)
{
if (si.BaseStream.CanWrite)
{
...
}
}
}
Inside of the block, I'm able to execute git commands like this:
si.WriteLine("cd \"P:/code/testapp\""); which navigates the git
shell to that directory
si.WriteLine("git rev-parse HEAD");
which will give me the latest hash of the repository at that location.
I can see the hash returned in the bash shell and can also read it in through the standard input like this var hash = so.ReadLine();
When I try to do si.WriteLint("git pull"); though, it asks for username and password, which I would expect, but that isn't done through the standard input/output. I can't us var prompt = so.ReadLine(); to get the prompt for username or password and I can't use si.WriteLine("username"); to input my password to the prompt.
I've tried a number of things to get this to work, but so far no luck.
WARNING: messy code ahead, just tried to quickly prototype something, not create a masterpiece
Attempt 1: used standard input/output redirects as shown above to try to write/read the prompts for username/password.
Attempt 2: attempt to get the ssh-agent process that is being utilized by the git bash and write to it
si.WriteLine("ssh-agent --help");
var sshAgentInfo = string.Empty;
var tmp = a.ReadLine();
while (a.Peek() != -1)
{
sshAgentInfo += tmp;
tmp = a.ReadLine();
}
var begPos = sshAgentInfo.IndexOf("SSH_AGENT_PID=", StringComparison.Ordinal);
begPos = begPos + "SSH_AGENT_PID=".Length;
var endPos = sshAgentInfo.IndexOf(";", begPos, StringComparison.Ordinal);
var processId = int.Parse(sshAgentInfo.Substring(begPos, endPos - begPos));
var sshProcess = Process.GetProcessById(processId);
sshProcess.StartInfo.RedirectStandardInput = true;
sshProcess.StartInfo.RedirectStandardInput = true;
using (var si1 = sshProcess.StandardInput) { ... }
Attempt 3: Utilize credential git config credential.helper wincred
This and four were very similar attempts. Again just trying to figure out how to set the password in either of these credential managers from the command line.
Attempt 4: Utilize Git-Credential-Manager-for-Windows
I've tried looking through the documentation here and it seems as though there is an enhancement request to do something along these lines.
Both of these seem to have similar problems to attempt one. The git shell seems to be invoking another process which handles the standard input and output separate from the git shell. The difference and hope I have for these last two though is, Is there a way to call into those credential managers directly to set username/passwords for different urls? In the .git directory, the config file has the credentials setting to tell it which manager to use and it stores the username and email in plain text.
If I invoke a git pull through the shell on the machine and enter my credentials once through that shell it seems to store them, but is there a way to allow users to enter their credentials through a website and then call into the manager through the command line to securely store that information for future automated use?
I also came across this. I haven't had a chance to try it yet, but is this a viable option for doing something like this?
Any help would be greatly appreciated.
I have added the ClientBuildManager.PrecompileApplicaiton to my Azure web role as described here. The web role will start, however, takes an extraordinary long time (15-20 mins), presumably from the amount of files it has to compile (18K+).
After the role starts, I hit the site, which doesn't appear to be any faster in initial startup.
When I RDP to my web role, I can see 2 separate folders in the Temporary ASP.NET Files, one containing all of my pre-compiled code (3K+ files), the other containing a smaller set of files matching those that would be used during my initial request (50 files).
From what I can tell, the site is pre-compiling, however, the actual requests are not leveraging this pre-compilation and doing a normal on-the-fly compilation as before when a request is made.
After viewing the above, I made another request to a different page within my site and confirmed the folder with 50 files increased to 58 files, telling me it is in fact still doing on-the-fly compiling. The other folder with 3K files remained unchanged.
Here is the code I am using for my pre-compilation in my OnStart method:
var siteName = RoleEnvironment.CurrentRoleInstance.Id + "_Web";
var mainSite = serverManager.Sites[siteName];
var rootVirtualPath = String.Format("/LM/W3SVC/{0}/ROOT/", mainSite.Id);
var clientBuildManager = new ClientBuildManager(rootVirtualPath, null);
clientBuildManager.PrecompileApplication();
Am I missing something else that would force the role to use my pre-compiled files?
Here's the full code that works, note one important difference is to NOT include trailing slash in appVirtualDir
using (var serverManager = new ServerManager())
{
string siteName = RoleEnvironment.CurrentRoleInstance.Id + "_" + "Web";
var siteId = serverManager.Sites[siteName].Id;
var appVirtualDir = $"/LM/W3SVC/{siteId}/ROOT"; // Do not end this with a trailing /
var clientBuildManager = new ClientBuildManager(appVirtualDir, null, null,
new ClientBuildManagerParameter
{
PrecompilationFlags = PrecompilationFlags.Default,
});
clientBuildManager.PrecompileApplication();
}
For quite a few days now I have been trying to get some custom Active Directory based authentication to work. It all works in theory but apparently my theory is wrong. Users who are logged on to a domain write a string token (e.g. PIN code) to their own property field in Active Directory (doesn't really matter which one, but I used primaryInternationISDNNumber for this) upon logging on to the ASP.NET application This PIN is always generated and written programmatically.
To explain it roughly, the web browser loads a Java applet which then loads a native DLL written in C++, which generates and writes the PIN to current user's Active Directory field. That DLL then returns the generated PIN to the applet which then passes it to the browser, which performs an AJAX call with the data returned to initiate the authentication. The application, which has got access to the AD, reads this field value for the connecting user object and checks if it matches with the one the user supplied. If PIN codes match, the user is successfully authenticated.
This is the sample code the ASP.NET application used to read the AD:
using (var de = new DirectoryEntry("LDAP://" + domainName))
{
using (var adSearch = new DirectorySearcher(de))
{
// Get user from active directory.
adSearch.Filter = "(sAMAccountName=" + userName.Trim().ToLower(CultureInfo.CurrentCulture) + ")";
var adSearchResult = adSearch.FindOne();
var entry = adSearchResult.GetDirectoryEntry();
var pinCodeProp = entry.Properties["primaryInternationISDNNumber"];
return pinCodeProp != null ? pinCodeProp.Value : string.Empty;
}
}
This works fine, often. But often is not acceptable. It needs to always be working.
The trouble is that the ASP.NET application sometimes gets the value which was previously in that field, not the actual value. As if there is some kind of caching. I have tried to add de.UsePropertyCache = false but that yielded the same results.
I have created two Win32 console applications for test purposes. One writes the PIN code, the other reads the PIN code. They always work fine!
I thought, this gotta be some problem with IIS application pool. So I have created a native DLL which gets loaded by the ASP.NET application using Platform Invoke. This DLL creates a new thread, calls CoInitialize and reads the PIN code. This is the code:
pszFqdn = argv[1];
pszUserName = argv[2];
pszPassword = argv[3];
IADs *pObject = NULL;
HRESULT hr = S_OK;
hr = CoInitialize(NULL);
if (SUCCEEDED(hr))
{
hr = ADsOpenObject(pszFqdn, pszUserName, pszPassword, ADS_SECURE_AUTHENTICATION, IID_IADs, (LPVOID*)&pObject);
if (SUCCEEDED(hr) && pObject)
{
VARIANT var;
VariantInit(&var);
hr = pObject->Get(CComBSTR("primaryInternationalISDNNumber"), &var);
if ((SUCCEEDED(hr) && var.bstrVal) || hr == 0x8000500d)
{
if (hr != 0x8000500d)
{
// convert the BSTR received to TCHAR array
std::wstring wsValue(var.bstrVal, SysStringLen(var.bstrVal));
// copy the received value to somewhere
// ... not relevant
}
VariantClear(&var);
}
pObject->Release();
}
}
CoUninitialize();
To my tremendous and unpleasant surprise, this code after a day of working properly, started returning the previous values, just like the managed code before!
So now I thought, alright, I wasn't able to escape the IIS application pool and since this gotta be a problem with IIS application pool, I will create a native Windows application which I will execute by using Process.Start method. I will return my PIN code by means of process exit code (since it's an integer anyway). The application uses the similar C++ code as the DLL above.
So I start my application, wait for it to finish, read the exit code. Returns the bad value!
But okay, I'd say, the process is started using the current user credentials, which is again IIS application pool. So I start the application under different credentials. And guess what..... it returns the old value again (?!?!?!).
And I thought Java was hell... Anyone has got any idea about what could possibly be going on here?
It was the replication indeed. As I didn't want to force the replication before reading the field (that would have been a time-expensive operation probably anyway), I came to an idea to read this field from each domain controller and check if any of them matches with the value supplied.
As it might prove helpful to someone, I did that using the following code.
var ctx = new DirectoryContext(
DirectoryContextType.DirectoryServer,
ipAddress,
userName, // in the form DOMAIN\UserName or else it would fail for a remote directory server
password);
var domain = Domain.GetDomain(ctx);
var values = new List<string>();
foreach (DomainController dc in domain.DomainControllers)
{
using (var entry =
new DirectoryEntry(
"LDAP://" + dc.IPAddress,
userName,
password))
{
using (var search = new DirectorySearcher(entry))
{
search.Filter = "(&(primaryInternationalISDNNumber=*)(sAMaccountName=" + userName + "))";
var result = search.FindOne();
var de = result.GetDirectoryEntry();
if (de.Properties["primaryInternationalISDNNumber"].Value != null)
{
values.Add(de.Properties["primaryInternationalISDNNumber"].Value.ToString());
}
}
}
}
I have an d-link dp-311p print server which provides the printer status(offline, paper out, etc) on it's interface.
How can i get this oid status ?? i'm trying to find through axence nettools but there is A LOT of keys and the descriptions are not friendly...
Also, i'm trying to use this code(c#) to access the print server status but no success...
please, need a light, i'm completely lost!
Tks everyone
I did it! Here is how:
Search for mib browser because I didn't know the oid of the print server status. Found This one, then, I created a console app like this
OLEPRNLib.SNMP snmp = new OLEPRNLib.SNMP();
int Retries = 1;
int TimeoutInMS = 2000;
string CommunityString = "public";
string IPAddressOfPrinter = "192.168.1.12";
string ALLINEED;
// Open the SNMP connect to the print server
snmp.Open(IPAddressOfPrinter, CommunityString, Retries, TimeoutInMS);
ALLINEED = snmp.Get(".1.3.6.1.4.1.11.2.3.9.1.1.3.0");
snmp.Close();
Console.Write(ALLINEED);
On my machine I made a reference on the COM tab of the Add Reference dialog to “oleprn 1.0 Type Library“ which lived in “c:\Windows\System32\oleprn.dll“
Hope this can help someone.
Tks