text: #!/usr/bin/env node
  // Copyright (c) Microsoft. All rights reserved.
  // Licensed under the MIT license. See LICENSE file in the project root for full license information.
  
  'use strict';
  
  var program = require('commander');
  var uuid = require('uuid');
  var inputError = require('./common.js').inputError;
  var serviceError = require('./common.js').serviceError;
  var printSuccess = require('./common.js').printSuccess;
  var printDevice = require('./common.js').printDevice;
  var getHostFromSas = require('./common.js').getHostFromSas;
  var getSas = require('./common.js').getSas;
  var Registry = require('azure-iothub').Registry;
  
  var info;
  
  program
    .description('Create a device identity in your IoT Hub device registry, either using the specified device id or JSON description.')
    .usage('[options] [device-id|device-json]')
    .option('-a, --auto', 'create a device with an auto-generated device id')
    .option('--certificateAuthority', 'create a device that is authenticated with a CA signed cert')
    .option('-c, --connection-string', '[deprecated] The connection string is now displayed by default')
    .option('-d, --display <property-filter>', 'comma-separated list of device properties that should be displayed')
    .option('-l, --login <connection-string>', 'connection string to use to authenticate with your IoT Hub instance')
    .option('--key1 <key>', 'specify the primary key for newly created device')
    .option('--key2 <key>', 'specify the secondary key for newly created device')
    .option('-r, --raw', 'use this flag to return raw JSON instead of pretty-printed output')
    .option('-x, --x509', 'generate an x509 certificate to authenticate the device')
    .option('--daysValid', 'number of days the x509 certificate should be valid for', parseInt)
    .option('--thumbprint1 <thumbprint>', 'specify the primary thumbprint of the x509 certificate')
    .option('--thumbprint2 <thumbprint>', 'specify the secondary thumbprint of the x509 certificate')
    .parse(process.argv);
  
  if (((program.key1 || program.key2) && (program.x509 || program.thumbprint1 || program.thumbprint2)) ||
      ((program.key1 || program.key2) && (program.certificateAuthority)) ||
      ((program.x509 || program.thumbprint1 || program.thumbprint2) && (program.certificateAuthority))) {
    inputError('A device can use only one of: x509 certificates, CA signed certificates, or symmetric keys to authenticate.');
  }
  
  if(program.daysValid && !program.x509) {
    inputError('The --daysValid option applies only with x509 certificates');
  }
  
  if (program.auto && program.args[0]) {
    inputError('You cannot use the \'--auto\' option if you specify the device id or description');
  } else if (!program.auto && !program.args[0]) {
    inputError('You must either use the \'--auto\' option or specify a device id or description');
  } else if (program.auto) {
    info = {
      deviceId: uuid.v4(),
      status: 'enabled'
    };
  } else {
    try {
      // 'create' command expects either deviceId or JSON device description
      info = (program.args[0].charAt(0) !== '{') ? { deviceId: program.args[0], status: 'enabled' } : JSON.parse(program.args[0]);
    }
    catch (e) {
      if (e instanceof SyntaxError) inputError('Device information isn\'t valid JSON');
      else throw e;
    }
  }
  
  if(program.x509) {
    if (program.thumbprint1 || program.thumbprint2) {
      info.authentication = {
        type: 'selfSigned',
        x509Thumbprint: {
          primaryThumbprint: program.thumbprint1,
          secondaryThumbprint: program.thumbprint2,
        }
      };
      console.log(JSON.stringify(info, null, 2));
      createDevice(info);
    } else {
      generateCertAndCreateDevice(info);
    }
  } else if (program.key1 || program.key2) {
    info.authentication = {
      type: 'sas',
      symmetricKey: {
        primaryKey: program.key1,
        secondaryKey: program.key2,
      }
    };
    createDevice(info);
  } else if (program.certificateAuthority) {
    info.authentication = {
      type: 'certificateAuthority'
    };
    createDevice(info);
  } else if (isMissingAuth(info)) {
    console.log('No authentication method given. Device will be created with auto-generated symmetric keys.');
    createDevice(info);
  } else {
    // Auth info provided as part of the JSON parameter
    createDevice(info);
  }
  
  function createDevice(deviceInfo) {
    var sas = getSas(program.login);
  
    var registry = Registry.fromSharedAccessSignature(sas);
    registry.create(deviceInfo, function (err, createdDeviceInfo) {
      if (err) serviceError(err);
      else {
        printSuccess('Created device ' + deviceInfo.deviceId);
        var host = getHostFromSas(sas);
        printDevice(createdDeviceInfo, host, program.display, program.raw);
      }
    });
  }
  
  function generateCertAndCreateDevice(deviceInfo) {
    var pem = require('pem');
    var fs = require('fs');
  
    var certFile = deviceInfo.deviceId + '-cert.pem';
    var keyFile = deviceInfo.deviceId + '-key.pem';
    var thumbprint = null;
  
    var certOptions = {
      selfSigned: true,
      days: program.daysValid || 365
    };
  
    pem.createCertificate(certOptions, function (err, result) {
      if (err) {
        inputError('You must have OpenSSL installed in your path for iothub-explorer to be able to generate x509 certificates');
      } else {
        fs.writeFileSync(certFile, result.certificate);
        fs.writeFileSync(keyFile, result.clientKey);
        printSuccess('Certificate File: ' + certFile);
        printSuccess('Key File: ' + keyFile);
        pem.getFingerprint(result.certificate, function (err, result) {
          thumbprint = result.fingerprint.replace(/:/g, '');
          deviceInfo.authentication = {
            x509Thumbprint: {
              primaryThumbprint: thumbprint
            }
          };
          createDevice(deviceInfo);
        });
      }
    });
  }
  
  function isMissingAuth(deviceInfo) {
    return deviceInfo.authentication ?
             deviceInfo.authentication.symmetricKeys ?
               deviceInfo.authentication.symmetricKeys.primaryKey || deviceInfo.authentication.symmetricKeys.secondaryKey
             : deviceInfo.authentication.x509Thumbprints ?
               deviceInfo.authentication.x509Thumbprints.primaryThumbprint || deviceInfo.authentication.x509Thumbprints.secondaryThumbprint
             : deviceInfo.authentication.type === 'certificateAuthority'
           : false;
  }