First commit, only with CLI tools

This commit is contained in:
2019-03-13 11:06:13 +01:00
commit e88675331c
22 changed files with 1690 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
**/thumbs.bd
*.bak
api.log
client/config.ps1
BD/database*
reports/
__pycache__/
.vscode/
cooks/*.yaml
Binary file not shown.
+51
View File
@@ -0,0 +1,51 @@
# What is "Powerful Computer Manager"?
Powerful Computer Manager is the super-simple-but-powerful program to automatizate tasks in groups of computers (In a Active Directory site, or not) in a unattended way.
It inteed to be a simple SSMS alternative, that inventarizes computers and install/remove software in it, tweak system of run arbitrary commands.
You only have to write simple _cooks_ that are small yaml text files, create groups of computers and assign _cooks_ to this computers, and will be run in the destination computers
# Disclaimer
I'm using this program in the company I work with 10 computers with Windows 8.1/10 with various software (MSI,EXE,...) and tweaking services, and it works right
It's not super-stable and error proof, then, I don't have any responsability if something crashes, but you can open a Issue and will see it
# Roadmap
GUI administration in Python to superseed Powershell CLI control program and be cross-platform
# Requirements
## Server
For server you can use Linux or Windows, and in theory any environment that can run Python 3 can work as server
- Python3 with:
- Falcon
- Waitress-serve
- Json (Python3 have it preinstalled)
- Yaml
Python3 deps can be installed it using pip install command (pip install waitress falcon pyyaml)
- For controlling the program, you have to use "control.ps1" using Powershell. Powershell comes with Windows, and can be installed (PWCore) in Linux and macOS (I didn't tested it).
## Clients
- Windows:
- Windows 7 SP1 absolute minimal, but its only officially tested in 8.1 and 10, Windows 7 is being old now.
- It needs Powershell 5.0+, lower versions doesn't have some commands, not tested. In Windows 7 and 8.1, you have to update Powershell. (With Powershell 3.0/4.0, some commands like Get-Package doesn't work)
You can update from here: https://docs.microsoft.com/en-us/powershell/scripting/setup/installing-windows-powershell?view=powershell-6#upgrading-existing-windows-powershell
- Linux:
- Not supported yet, but when Windows be stable, I will try to do it in some way
# Instalation. Example installing Server and using a GPO of Active Directory/Task Schedule to make it work in clients
- Copy this folder to a folder in your server
- Install dependencies
- Open sysopt.py file and change values if needed
- Use bat file if you are in Windows to run the python server, or sh file if you are in Linux environment
- Go to Client folder and copy "configexample.ps1" to "config.ps1" and change their values to yours.
- For clients to work, they have to run "client\client.ps1" every 30 mins/1 hour or so, I did using Active Directory and a Task Scheduled GPO (Copying client.ps1 and config.ps1 files from Client to SysVol\DomainName\Scripts\)
- User: SYSTEM (Or NT\SYSTEM)
- Command: powershell.exe
- Args: -executionpolicy bypass -windowstyle hidden -noninteractive -nologo -file "\\SERVER\SysVol\DOMAINNAME\scripts\client.ps1"
Note: client.ps1 has to have control.ps1 file with it
- If you don't have Active Directory, you can do the Task Scheduled task in every computer without problems, and using another Shared folder, it will work
+541
View File
@@ -0,0 +1,541 @@
#!/usr/bin/python3
# Build 5
##It can be run directly with "waitress-serve --port=3333 api:api"
import falcon
import random
import os
import time
import sql # SQL work file
import json,yaml
from datetime import datetime
import sysopt #File of options
def logit(text):
text = str(text)
now = datetime.now()
print ("Log: " + text)
with open('api.log', 'a') as file:
file.write("Log ("+now.strftime("%x %X") + "): ")
file.write(text)
file.write('\n')
class getComputerExists(object):
def on_get(self, request, response):
logit(request)
ComputerName,UUID = None, None
for key, value in request.params.items():
if key == "ComputerName":
ComputerName = value
if key == "UUID":
UUID = value
if ComputerName is None:
response.media = {'RESULT': '0'}
else:
result = sql.select("SELECT COUNT(*) 'RESULT' FROM COMPUTERS WHERE Name='"+ComputerName+"' AND UUID='"+UUID+"'")
if result[0]['RESULT'] == 0: # 0 or 1
if sql.select("SELECT COUNT(*) 'RESULT' FROM COMPUTERS WHERE Name='"+ComputerName+"' AND UUID IS NULL")[0]['RESULT'] != 0:
sql.insert("UPDATE COMPUTERS SET UUID='"+UUID+"' WHERE Name='"+ComputerName+"'")
response.media = {'RESULT': '1'}
return
if sysopt.addComputers == True:
sql.insert("INSERT INTO COMPUTERS (`Name`,`UUID`) VALUES('"+ComputerName+"','"+UUID+"')")
response.media = {'RESULT': '1'}
else:
response.media = {'RESULT': '0'}
else: #Exists
response.media = {'RESULT': '1'}
class getGroups(object): #Get local groups of a Computer
def on_get(self,request, response):
logit(request)
ComputerID=None
for key, value in request.params.items():
if key == "ComputerName":
ComputerID = str(sql.select("SELECT ID_C FROM COMPUTERS WHERE Name='"+value+"'")[0]['ID_C'])
if key == "ComputerID":
ComputerID = value
if ComputerID is None:
data = sql.select("SELECT * FROM GROUPS")
response.media = data
else:
data = sql.select("SELECT * FROM GROUPS WHERE (SELECT ID_G FROM COMPUTER_GROUP WHERE ID_C = '"+ComputerID+"')")
response.media = data
class getComputers(object):
def on_get(self,request, response):
logit(request)
ComputerID=None
for key, value in request.params.items():
if key == "ComputerName":
ComputerID = str(sql.select("SELECT ID_C FROM COMPUTERS WHERE Name='"+value+"'")[0]['ID_C'])
if key == "ComputerID":
ComputerID = value
if ComputerID is None:
data = sql.select("SELECT * FROM COMPUTERS")
response.media = data
else:
data = sql.select("SELECT * FROM COMPUTERS WHERE ID_C = '"+ComputerID+"')")
response.media = data
class getComputersGrp(object): #List of computers in a group
def on_get(self,request, response):
logit(request)
GroupID=None
for key, value in request.params.items():
if key == "GroupName":
GroupID = str(sql.select("SELECT ID_G FROM GROUPS WHERE Name='"+value+"'")[0]['ID_G'])
if key == "GroupID":
GroupID = value
data = sql.select("SELECT * FROM COMPUTERS WHERE ID_C IN (SELECT ID_C FROM COMPUTER_GROUP WHERE ID_G='"+GroupID+"') ORDER BY Name")
response.media = data
class getCook(object): # Get the list of cooks for a computer to implement (Var all=1 for see all of, implemented or not)
def on_get(self,request, response):
logit(request)
ComputerID,GroupID, SeeAll =None, None, None
for key, value in request.params.items():
if key == "ComputerName":
ComputerID = str(sql.select("SELECT ID_C FROM COMPUTERS WHERE Name='"+value+"'")[0]['ID_C'])
if key == "ComputerID":
ComputerID = value
if key == "GroupName":
GroupID = str(sql.select("SELECT ID_G FROM GROUPS WHERE Name='"+value+"'")[0]['ID_G'])
if key == "GroupID":
GroupID = value
if key == "SeeAll":
SeeAll = str(value)
if ComputerID is None and GroupID is None:
response.media = {'ERROR': 'I need a Group or Computer to search'}
elif ComputerID is not None and SeeAll is None:
data = sql.select("SELECT * FROM COOKS_IDG WHERE ID_G IN (SELECT ID_G FROM COMPUTER_GROUP WHERE ID_C = '"+ComputerID+"')") #All cooks for this computer
fordelete = []
for i in range(len(data)):
for key, value in data[i].items(): #Iterate in a dict (json)
if key == 'CookName':
CookName = value #Name
with open('cooks/'+CookName+'.yaml', 'r') as myfile:
filecook = myfile.read()
coun = sql.select("SELECT COUNT(*) 'RESULT' FROM COOKS_STATUS WHERE ID_C='"+ComputerID+"' AND CookName='"+CookName+"' AND Revision='"+str(yaml.safe_load(filecook)['revision'])+"' AND `Error`='0'")
if coun[0]['RESULT'] == 1:
fordelete.append(i) #Its good, do not say to client
for x in reversed(fordelete): #Deleting cooks that are implemented in client. Reverse order (Because is an array and index..)
data.pop(x)
response.media = data
else:
response.media = sql.select("SELECT * FROM COOKS_IDG WHERE ID_G IN (SELECT ID_G FROM COMPUTER_GROUP WHERE ID_C = '"+ComputerID+"')") #All cooks for this computer
class setCook(object): #Assign Cook to group
def on_get(self, request, response):
logit(request)
GroupID, CookName = None, None # Initialize
for key, value in request.params.items():
if key == "CookName":
exists = os.path.isfile('cooks/'+value+'.yaml')
CookName = value
if key == "GroupName":
GroupID = str(sql.select("SELECT ID_G FROM GROUPS WHERE Name='"+value+"'")[0]['ID_G'])
if key == "GroupID":
GroupID = value
if GroupID is None or exists is False:
response.media = {'ERROR': 'GroupID is not defined or Cook not exists'}
elif int(sql.select("SELECT COUNT(*) 'COUNT' FROM COOKS_IDG WHERE `ID_G`='"+GroupID+"' AND `CookName`='"+CookName+"'")[0]['COUNT']) > 0:
response.media = {'WARNING': 'This union GROUP-CookName exists'}
else:
result = sql.insert("INSERT INTO COOKS_IDG (`ID_G`,`CookName`) VALUES ('"+GroupID+"','"+CookName+"')")
response.media = result
class delCookGrp(object): #Delete cook from a group
def on_get(self,request,response):
logit(request)
GroupID, CookName = None, None # Initialize
for key, value in request.params.items():
if key == "CookName":
exists = os.path.isfile('cooks/'+value+'.yaml')
if exists:
CookName = value
if key == "GroupName":
GroupID = str(sql.select("SELECT ID_G FROM GROUPS WHERE Name='"+value+"'")[0]['ID_G'])
if key == "GroupID":
GroupID = value
if GroupID is not None and CookName is not None:
result = sql.insert("DELETE FROM COOKS_IDG WHERE ID_G='"+GroupID+"' AND CookName='"+CookName+"'")
response.media = result
else:
response.media = {'RESULT': 'Error, no Group, or CookName does\'t exists'}
class delEmptyPcsGroup(object): #Delete all computers from a group
def on_get(self,request,response):
logit(request)
GroupID = None # Initialize
for key, value in request.params.items():
if key == "GroupName":
GroupID = str(sql.select("SELECT ID_G FROM GROUPS WHERE Name='"+value+"'")[0]['ID_G'])
if key == "GroupID":
GroupID = value
if GroupID is not None:
result = sql.insert("DELETE FROM COMPUTER_GROUP WHERE ID_G='"+GroupID+"'")
response.media = result
else:
response.media = {'RESULT': 'Error, this group doesn\'t exists'}
class getCookGrp(object): # Get cooks from a Group
def on_get(self,request,response):
logit(request)
GroupID = None # Initialize
for key, value in request.params.items():
if key == "GroupName":
GroupID = str(sql.select("SELECT ID_G FROM GROUPS WHERE Name='"+value+"'")[0]['ID_G'])
if key == "GroupID":
GroupID = value
if GroupID is not None:
result = sql.select("SELECT * FROM COOKS_IDG WHERE ID_G='"+GroupID+"'")
response.media = result
else:
response.media = {'RESULT': 'Error, no Group selected'}
class getGrpCook(object): # Get Groups of a Cook
def on_get(self,request,response):
logit(request)
CookName = None # Initialize
for key, value in request.params.items():
if key == "CookName":
exists = os.path.isfile('cooks/'+value+'.yaml')
if exists:
CookName = value
if CookName is not None:
response.media = sql.select("SELECT Name FROM GROUPS WHERE ID_G IN (SELECT ID_G FROM COOKS_IDG WHERE CookName='"+CookName+"')")
else:
response.media = {'RESULT': 'Error, no Cook selected'}
class getStatusCook(object): # Get Status of a Cook (If Brief=1 is sent too, brief status (Completed:X, updated:X..))
def on_get(self,request,response):
logit(request)
CookName = None # Initialize
for key, value in request.params.items():
if key == "CookName":
exists = os.path.isfile('cooks/'+value+'.yaml')
if exists:
CookName = value
if CookName is not None:
response.media = sql.select("SELECT Name, Revision, Error FROM COMPUTERS,COOKS_STATUS WHERE CookName = '"+CookName+"' AND COMPUTERS.ID_C=COOKS_STATUS.ID_C")
else:
response.media = {'RESULT': 'Error, no Cook selected'}
class getLastRevisionCook(object): # Get Number Revision (Revision=X)
def on_get(self,request,response):
logit(request)
CookName = None # Initialize
for key, value in request.params.items():
if key == "CookName":
exists = os.path.isfile('cooks/'+value+'.yaml')
if exists:
CookName = value
if CookName is not None:
with open('cooks/'+CookName+'.yaml', 'r') as myfile:
filecook = myfile.read()
response.media = {'Revision': str(yaml.safe_load(filecook)['revision'])}
else:
response.media = {'RESULT': 'Error, no Cook selected'}
class addComputer(object):
def on_get(self, request, response):
logit(request)
ComputerName = None
for key,value in request.params.items():
if key == "ComputerName":
ComputerName = value
if ComputerName is None:
response.media = {'RESULT': 'Error, you need a ComputerName to add'}
else:
result = sql.insert("INSERT INTO COMPUTERS (Name) VALUES ('"+ComputerName+"')")
response.media = result
class addGroup(object):
def on_get(self, request, response):
logit(request)
GroupName= None
for key,value in request.params.items():
if key == "GroupName":
GroupName = value
if GroupName is None:
response.media = {'RESULT': 'Error, you need a GroupName to add'}
else:
result = sql.insert("INSERT INTO GROUPS (Name) VALUES ('"+GroupName+"')")
response.media = result
class delGroup(object): #Delete group
def on_get(self, request, response):
logit(request)
GroupID= None
for key,value in request.params.items():
if key == "GroupID":
GroupID = value
if key == "GroupName":
GroupID = str(sql.select("SELECT ID_G FROM GROUPS WHERE Name='"+value+"'")[0]['ID_G'])
if GroupID is None:
response.media = {'RESULT': 'Error, you need a GroupName to delete'}
else:
sql.insert("DELETE FROM COMPUTER_GROUP WHERE ID_G='"+GroupID+"'")
sql.insert("DELETE FROM COOKS_IDG WHERE ID_G='"+GroupID+"'")
result = sql.insert("DELETE FROM GROUPS WHERE ID_G='"+GroupID+"'")
response.media = result
class updGroup(object): #Delete group
def on_get(self, request, response):
logit(request)
GroupID, GroupNewName= None, None
for key,value in request.params.items():
if key == "GroupID":
GroupID = value
if key == "GroupName":
GroupID = str(sql.select("SELECT ID_G FROM GROUPS WHERE Name='"+value+"'")[0]['ID_G'])
if key == "GroupNewName":
Count = str(sql.select("SELECT COUNT(*) 'Count' FROM GROUPS WHERE Name='"+value+"'")[0]['Count'])
if Count == "0":
GroupNewName = value
else:
response.media = {'RESULT': 'Error, New group name exists'}
if GroupID is None or GroupNewName is None:
response.media = {'RESULT': 'Error, you need a GroupName and new name to update name'}
else:
result = sql.insert("UPDATE GROUPS SET Name='"+GroupNewName+"' WHERE ID_G='"+GroupID+"'")
response.media = result
class addGrpComputer(object): #Add computer to a group (Local)
def on_get(self, request, response):
logit(request)
ComputerID, GroupID= None, None
for key, value in request.params.items():
if key == "ComputerName":
ComputerID = str(sql.select("SELECT ID_C FROM COMPUTERS WHERE Name='"+value+"'")[0]['ID_C'])
if key == "ComputerID":
ComputerID = value
if key == "GroupName":
GroupID = str(sql.select("SELECT ID_G FROM GROUPS WHERE Name='"+value+"'")[0]['ID_G'])
if key == "GroupID":
GroupID = value
if ComputerID is None or GroupID is None:
response.media = {'RESULT': 'Error, you need a Name and Group to add'}
else:
result = sql.insert("INSERT INTO COMPUTER_GROUP (ID_C,ID_G) VALUES ('"+ComputerID+"','"+GroupID+"')")
response.media = result
class delGrpComputer(object): #Del computer from a group
def on_get(self, request, response):
logit(request)
ComputerID, GroupID= None, None
for key, value in request.params.items():
if key == "ComputerName":
ComputerID = str(sql.select("SELECT ID_C FROM COMPUTERS WHERE Name='"+value+"'")[0]['ID_C'])
if key == "ComputerID":
ComputerID = value
if key == "GroupName":
GroupID = str(sql.select("SELECT ID_G FROM GROUPS WHERE Name='"+value+"'")[0]['ID_G'])
if key == "GroupID":
GroupID = value
if ComputerID is None or GroupID is None:
response.media = {'RESULT': 'Error, you need a Name and Group to add'}
else:
result = sql.insert("DELETE FROM COMPUTER_GROUP WHERE ID_C='"+ComputerID+"' AND ID_G='"+GroupID+"'")
response.media = result
class delComputer(object): #Delete computer
def on_get(self, request, response):
logit(request)
ComputerID= None
for key, value in request.params.items():
if key == "ComputerName":
ComputerID = str(sql.select("SELECT ID_C FROM COMPUTERS WHERE Name='"+value+"'")[0]['ID_C'])
if key == "ComputerID":
ComputerID = value
if ComputerID is None:
response.media = {'RESULT': 'Error, you need a Name/ComputerID to update data'}
else:
sql.insert("DELETE FROM COMPUTER_GROUP WHERE ID_C='"+ComputerID+"'")
sql.insert("DELETE FROM COOKS_STATUS WHERE ID_C='"+ComputerID+"'")
resp = sql.insert("DELETE FROM COMPUTERS WHERE ID_C='"+ComputerID+"'")
logit(resp)
class updComputer(object):
def on_get(self, request, response):
logit(request)
ComputerID, UUID= None, None
for key, value in request.params.items():
if key == "ComputerName":
try:
ComputerID = str(sql.select("SELECT ID_C FROM COMPUTERS WHERE Name='"+value+"'")[0]['ID_C'])
except:
response.media = {'RESULT': 'Error: Computer not exists in database'}
break
if key == "ComputerID":
ComputerID = value
if key == "UUID":
UUID = value
if ComputerID is None and response.media is None:
response.media = {'RESULT': 'Error, you need a ComputerName/ComputerID to update data'}
elif response.media is None and ComputerID is not None and UUID is not None:
Count = str(sql.select("SELECT COUNT(*) 'Count' FROM COMPUTERS WHERE UUID='"+UUID+"' AND ID_C='"+ComputerID+"'")[0]['Count'])
if Count == "0":
response.media = {'RESULT': 'Error, computer doesn\'t exists in database'}
else:
for key, value in request.params.items():
if key == "SOVersion":
response.media = sql.insert("UPDATE COMPUTERS SET SOVersion ='"+value+"' WHERE ID_C='"+ComputerID+"'")
if key == "SOBit":
response.media = sql.insert("UPDATE COMPUTERS SET SOBit ='"+value+"' WHERE ID_C='"+ComputerID+"'")
if key == "SOCaption":
response.media = sql.insert("UPDATE COMPUTERS SET SOCaption ='"+value+"' WHERE ID_C='"+ComputerID+"'")
if key == "LastConnection":
response.media = sql.insert("UPDATE COMPUTERS SET LastConnection ='"+value+"' WHERE ID_C='"+ComputerID+"'")
if key == "LastUser":
response.media = sql.insert("UPDATE COMPUTERS SET LastUser='"+value+"' WHERE ID_C='"+ComputerID+"'")
if key == "RAM":
response.media = sql.insert("UPDATE COMPUTERS SET RAM='"+value+"' WHERE ID_C='"+ComputerID+"'")
if key == "CPUName":
response.media = sql.insert("UPDATE COMPUTERS SET CPUName='"+value+"' WHERE ID_C='"+ComputerID+"'")
class updCookName(object):
def on_get(self, request, response):
logit(request)
CookName, CookNewName= None, None #Initialize
for key, value in request.params.items():
if key == "CookName":
if os.path.isfile('cooks/'+value+'.yaml'):
CookName= value
else:
response.media = {'RESULT': 'Error: Cook not exists in folder'}
break
if key == "CookNewName":
if os.path.isfile('cooks/'+value+'.yaml'):
response.media = {'RESULT': 'Error: There is a cook with the new name in folder!'}
break
else:
CookNewName= value
if CookName is None or CookNewName is None:
response.media = {'RESULT': 'Error, you need the old and new Cook Name to update data'}
elif response.media is None:
old_file = os.path.join("cooks", CookName+'.yaml')
new_file = os.path.join("cooks", CookNewName+'.yaml')
os.rename(old_file, new_file)
sql.insert("UPDATE COOKS_IDG SET CookName ='"+CookNewName+"' WHERE CookName='"+CookName+"'")
response.media = sql.insert("UPDATE COOKS_STATUS SET CookName ='"+CookNewName+"' WHERE CookName='"+CookName+"'")
class loadCook(object):
def on_get(self, request, response):
logit(request)
CookName, ComputerID, UUID= None, None, None #Initialize
for key, value in request.params.items():
if key == "CookName":
if os.path.isfile('cooks/'+value+'.yaml'):
CookName= value
else:
response.media = {'RESULT': 'Error: Cook not exists in folder'}
break
if key == "ComputerName":
ComputerID = str(sql.select("SELECT ID_C FROM COMPUTERS WHERE Name='"+value+"'")[0]['ID_C'])
if key == "ComputerID":
ComputerID = value
if key == "UUID":
UUID = value
if CookName is None and response.media is None:
response.media = {'RESULT': 'Error, you need a CookName to load it'}
elif response.media is None:
Count = str(sql.select("SELECT COUNT(*) 'Count' FROM COMPUTERS WHERE UUID='"+UUID+"' AND ID_C='"+ComputerID+"'")[0]['Count'])
if Count == "0":
response.media = {'RESULT': 'Error, computer doesn\'t exists in database'}
else:
with open('cooks/'+CookName+'.yaml', 'r') as myfile:
data = myfile.read()
response.media = yaml.safe_load(data)
class setCookStatus(object):
def on_get(self, request, response):
logit(request)
CookName, ComputerID, Revision, Error,ErrorDesc= None, None, None, None, ""
for key, value in request.params.items():
if key == "CookName":
if os.path.isfile('cooks/'+value+'.yaml'): # You have to know that cook exists.
CookName= value
else:
response.media = {'RESULT': 'Error: Cook not exists in folder'}
break
if key == "ComputerName":
ComputerID = str(sql.select("SELECT ID_C FROM COMPUTERS WHERE Name='"+value+"'")[0]['ID_C'])
if key == "ComputerID":
ComputerID = value
if key == "Revision":
Revision = value
if key == "Error":
Error = value
if key == "ErrorDesc":
ErrorDesc = value
if CookName is None and response.media is None:
response.media = {'RESULT': 'Error, you need a CookName to load it'}
elif response.media is None and CookName is not None and ComputerID is not None and Revision is not None and Error is not None:
statt = sql.select("SELECT COUNT(*) 'RESULT' FROM COOKS_STATUS WHERE CookName='"+CookName+"' AND ID_C='"+ComputerID+"'")[0]['RESULT']
if statt == 0:
#INSERT, NEW
response.media = sql.insert("INSERT INTO COOKS_STATUS (CookName,ID_C,Revision,`Error`,`ErrorDesc`) VALUES ('"+CookName+"', '"+ComputerID+"', '"+Revision+"','"+Error+"','"+ErrorDesc+"')")
else:
#UPDATE, NOT NEW
response.media = sql.insert("UPDATE COOKS_STATUS SET Revision='"+Revision+"',`Error`='"+Error+"',`ErrorDesc`='"+ErrorDesc+"' WHERE ID_C='"+ComputerID+"' AND CookName='"+CookName+"'")
else:
response.media = {'RESULT': 'Error in parameters...'}
logit(response.media)
api = falcon.API()
api.add_route('/get/computers', getComputers()) #Get list of computer
api.add_route('/get/computerexists', getComputerExists()) #Returns 0 or 1 (name status)
api.add_route('/get/groups', getGroups()) #Get groups of a computer (Or list if not args)
api.add_route('/get/computersgrp', getComputersGrp()) #Get computers in a group
api.add_route('/get/cook', getCook()) #Get cooks from a group
api.add_route('/set/cook', setCook()) #Assign cook to group
api.add_route('/del/cookgrp', delCookGrp()) # Deassign cook from a group
api.add_route('/del/emptypcsgroup', delEmptyPcsGroup()) # Delete all computers from a group
api.add_route('/get/cookgrp', getCookGrp()) # See cooks that have a determinated group
api.add_route('/get/grpcook', getGrpCook()) # See groups of a determinated cook
api.add_route('/get/statuscook', getStatusCook()) # See status of a cook
api.add_route('/get/lastrevisioncook', getLastRevisionCook()) # Returns number of last revision of a cook
api.add_route('/add/computer', addComputer()) #Add computer
api.add_route('/del/computer', delComputer()) #Delete computer
api.add_route('/add/group', addGroup()) #Add group to the list of local groups
api.add_route('/del/group', delGroup()) #Delete group
api.add_route('/upd/group', updGroup()) #Update group name
api.add_route('/add/grpcomputer', addGrpComputer()) #Add computer to a group
api.add_route('/del/grpcomputer', delGrpComputer()) #Delete computer from a group
api.add_route('/upd/computer', updComputer()) #Update data of computer
api.add_route('/upd/cookname', updCookName()) #Update file name of cook and SQL references to it.
api.add_route('/load/cook', loadCook()) # Load a cook (Transfer in json way)
api.add_route('/set/cookstatus', setCookStatus()) # Update status of a cook in a computer
+76
View File
@@ -0,0 +1,76 @@
$root = $PSCommandPath | Split-Path -Parent
. $root\config.ps1
$table = (Invoke-RestMethod -Method Get -Uri "$server/get/computers") # Get all computers
$DNSPrefix = (get-DnsClientGlobalSetting).SuffixSearchList[0] # Get DNS Prefix, needed to search computers in some environments
$array = @()
function getPrograms($computername) {
#Define the variable to hold the location of Currently Installed Programs
$UninstallKey="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
#Create an instance of the Registry Object and open the HKLM base key
$reg=[microsoft.win32.registrykey]::OpenRemoteBaseKey('LocalMachine',$computername)
#Drill down into the Uninstall key using the OpenSubKey Method
$regkey=$reg.OpenSubKey($UninstallKey)
#Retrieve an array of string that contain all the subkey names
$subkeys=$regkey.GetSubKeyNames()
#Open each Subkey and use GetValue Method to return the required values for each
$allprogs = @()
foreach($key in $subkeys){
$thisKey=$UninstallKey+"\\"+$key
$thisSubKey=$reg.OpenSubKey($thisKey)
$obj = New-Object PSObject
$obj | Add-Member -MemberType NoteProperty -Name "ComputerName" -Value $computername
$obj | Add-Member -MemberType NoteProperty -Name "DisplayName" -Value $($thisSubKey.GetValue("DisplayName"))
$obj | Add-Member -MemberType NoteProperty -Name "DisplayVersion" -Value $($thisSubKey.GetValue("DisplayVersion"))
$obj | Add-Member -MemberType NoteProperty -Name "InstallLocation" -Value $($thisSubKey.GetValue("InstallLocation"))
$obj | Add-Member -MemberType NoteProperty -Name "Publisher" -Value $($thisSubKey.GetValue("Publisher"))
$allprogs += $obj
}
return $allprogs
}
foreach ($comp in $table){
$name = -join($comp.Name,".",$DNSPrefix);
if (Test-Connection -ComputerName $name -Quiet){
Write-Output $name
$array += getPrograms($name)
}else{
Write-Output "$name Offline"
}
}
#$array | Where-Object { $_.DisplayName } | select ComputerName, DisplayName, DisplayVersion, Publisher | ft -auto
$array | Where-Object { $_.DisplayName } | select ComputerName, DisplayName, DisplayVersion | export-csv "$PSScriptRoot\..\Reports\software.csv" -NoTypeInformation -Delimiter ";" -Encoding ASCII
Write-Host "Guardado en ..\Reports\software.csv"
#$content = [System.IO.File]::ReadAllText("$PSScriptRoot\Reports\software.csv").Replace("A","")
#[System.IO.File]::WriteAllText("$PSScriptRoot\Reports\software.csv", $content)
+310
View File
@@ -0,0 +1,310 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<wxFormBuilder_Project>
<FileVersion major="1" minor="15" />
<object class="Project" expanded="1">
<property name="class_decoration">; </property>
<property name="code_generation">C++</property>
<property name="disconnect_events">1</property>
<property name="disconnect_mode">source_name</property>
<property name="disconnect_php_events">0</property>
<property name="disconnect_python_events">0</property>
<property name="embedded_files_path">res</property>
<property name="encoding">UTF-8</property>
<property name="event_generation">connect</property>
<property name="file"></property>
<property name="first_id">1000</property>
<property name="help_provider">none</property>
<property name="indent_with_spaces"></property>
<property name="internationalize">0</property>
<property name="name">MyProject1</property>
<property name="namespace"></property>
<property name="path">.</property>
<property name="precompiled_header"></property>
<property name="relative_path">1</property>
<property name="skip_lua_events">1</property>
<property name="skip_php_events">1</property>
<property name="skip_python_events">1</property>
<property name="ui_table">UI</property>
<property name="use_enum">0</property>
<property name="use_microsoft_bom">0</property>
<object class="Frame" expanded="1">
<property name="aui_managed">0</property>
<property name="aui_manager_style">wxAUI_MGR_DEFAULT</property>
<property name="bg"></property>
<property name="center">wxBOTH</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="enabled">1</property>
<property name="event_handler">impl_virtual</property>
<property name="extra_style"></property>
<property name="fg"></property>
<property name="font"></property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="maximum_size"></property>
<property name="minimum_size"></property>
<property name="name">MainFrame</property>
<property name="pos"></property>
<property name="size">749,396</property>
<property name="style">wxDEFAULT_FRAME_STYLE</property>
<property name="subclass">; ; forward_declare</property>
<property name="title"></property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style">wxTAB_TRAVERSAL</property>
<property name="xrc_skip_sizer">1</property>
<object class="wxMenuBar" expanded="1">
<property name="bg"></property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="font"></property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">MyMenuBar</property>
<property name="maximum_size"></property>
<property name="minimum_size"></property>
<property name="name">m_menubar1</property>
<property name="permission">protected</property>
<property name="pos"></property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<object class="wxMenu" expanded="1">
<property name="label">Equipos</property>
<property name="name">m_computers</property>
<property name="permission">protected</property>
<object class="wxMenuItem" expanded="1">
<property name="bitmap"></property>
<property name="checked">0</property>
<property name="enabled">1</property>
<property name="help"></property>
<property name="id">wxID_ANY</property>
<property name="kind">wxITEM_NORMAL</property>
<property name="label">Añadir equipo</property>
<property name="name">scomp_addc</property>
<property name="permission">none</property>
<property name="shortcut"></property>
<property name="unchecked_bitmap"></property>
</object>
<object class="wxMenuItem" expanded="1">
<property name="bitmap"></property>
<property name="checked">0</property>
<property name="enabled">1</property>
<property name="help"></property>
<property name="id">wxID_ANY</property>
<property name="kind">wxITEM_NORMAL</property>
<property name="label">Borrar equipo</property>
<property name="name">scomp_deletec</property>
<property name="permission">none</property>
<property name="shortcut"></property>
<property name="unchecked_bitmap"></property>
</object>
</object>
<object class="wxMenu" expanded="1">
<property name="label">Grupos</property>
<property name="name">m_groups</property>
<property name="permission">protected</property>
<object class="wxMenuItem" expanded="1">
<property name="bitmap"></property>
<property name="checked">0</property>
<property name="enabled">1</property>
<property name="help"></property>
<property name="id">wxID_ANY</property>
<property name="kind">wxITEM_NORMAL</property>
<property name="label">Añadir Grupo</property>
<property name="name">sgroup_addg</property>
<property name="permission">none</property>
<property name="shortcut"></property>
<property name="unchecked_bitmap"></property>
</object>
<object class="wxMenuItem" expanded="1">
<property name="bitmap"></property>
<property name="checked">0</property>
<property name="enabled">1</property>
<property name="help"></property>
<property name="id">wxID_ANY</property>
<property name="kind">wxITEM_NORMAL</property>
<property name="label">Borrar grupo</property>
<property name="name">sgroup_delg</property>
<property name="permission">none</property>
<property name="shortcut"></property>
<property name="unchecked_bitmap"></property>
</object>
<object class="wxMenuItem" expanded="1">
<property name="bitmap"></property>
<property name="checked">0</property>
<property name="enabled">1</property>
<property name="help"></property>
<property name="id">wxID_ANY</property>
<property name="kind">wxITEM_NORMAL</property>
<property name="label">Ver equipos de un grupo</property>
<property name="name">sgroup_seeg</property>
<property name="permission">none</property>
<property name="shortcut"></property>
<property name="unchecked_bitmap"></property>
</object>
<object class="wxMenuItem" expanded="1">
<property name="bitmap"></property>
<property name="checked">0</property>
<property name="enabled">1</property>
<property name="help"></property>
<property name="id">wxID_ANY</property>
<property name="kind">wxITEM_NORMAL</property>
<property name="label">Vaciar grupo</property>
<property name="name">sgroup_emptyg</property>
<property name="permission">none</property>
<property name="shortcut"></property>
<property name="unchecked_bitmap"></property>
</object>
<object class="wxMenuItem" expanded="1">
<property name="bitmap"></property>
<property name="checked">0</property>
<property name="enabled">1</property>
<property name="help"></property>
<property name="id">wxID_ANY</property>
<property name="kind">wxITEM_NORMAL</property>
<property name="label">Cambiar nombre de un grupo</property>
<property name="name">sgroup_reng</property>
<property name="permission">none</property>
<property name="shortcut"></property>
<property name="unchecked_bitmap"></property>
</object>
<object class="wxMenuItem" expanded="1">
<property name="bitmap"></property>
<property name="checked">0</property>
<property name="enabled">1</property>
<property name="help"></property>
<property name="id">wxID_ANY</property>
<property name="kind">wxITEM_NORMAL</property>
<property name="label">Recetas asignadas a un grupo</property>
<property name="name">sgroup_cooksg</property>
<property name="permission">none</property>
<property name="shortcut"></property>
<property name="unchecked_bitmap"></property>
</object>
</object>
<object class="wxMenu" expanded="1">
<property name="label">Recetas</property>
<property name="name">m_cooks</property>
<property name="permission">protected</property>
<object class="wxMenuItem" expanded="1">
<property name="bitmap"></property>
<property name="checked">0</property>
<property name="enabled">1</property>
<property name="help"></property>
<property name="id">wxID_ANY</property>
<property name="kind">wxITEM_NORMAL</property>
<property name="label">Recetas de un grupo</property>
<property name="name">scook_cooksg</property>
<property name="permission">none</property>
<property name="shortcut"></property>
<property name="unchecked_bitmap"></property>
</object>
<object class="wxMenuItem" expanded="1">
<property name="bitmap"></property>
<property name="checked">0</property>
<property name="enabled">1</property>
<property name="help"></property>
<property name="id">wxID_ANY</property>
<property name="kind">wxITEM_NORMAL</property>
<property name="label">Añadir receta a un grupo</property>
<property name="name">scook_addcookg</property>
<property name="permission">none</property>
<property name="shortcut"></property>
<property name="unchecked_bitmap"></property>
</object>
<object class="wxMenuItem" expanded="1">
<property name="bitmap"></property>
<property name="checked">0</property>
<property name="enabled">1</property>
<property name="help"></property>
<property name="id">wxID_ANY</property>
<property name="kind">wxITEM_NORMAL</property>
<property name="label">Borrar receta de un grupo</property>
<property name="name">scook_deletecookg</property>
<property name="permission">none</property>
<property name="shortcut"></property>
<property name="unchecked_bitmap"></property>
</object>
<object class="wxMenuItem" expanded="1">
<property name="bitmap"></property>
<property name="checked">0</property>
<property name="enabled">1</property>
<property name="help"></property>
<property name="id">wxID_ANY</property>
<property name="kind">wxITEM_NORMAL</property>
<property name="label">Renombrar receta</property>
<property name="name">scook_renamecook</property>
<property name="permission">none</property>
<property name="shortcut"></property>
<property name="unchecked_bitmap"></property>
</object>
<object class="wxMenuItem" expanded="1">
<property name="bitmap"></property>
<property name="checked">0</property>
<property name="enabled">1</property>
<property name="help"></property>
<property name="id">wxID_ANY</property>
<property name="kind">wxITEM_NORMAL</property>
<property name="label">Detalles de implantación de una receta</property>
<property name="name">scook_detailscook</property>
<property name="permission">none</property>
<property name="shortcut"></property>
<property name="unchecked_bitmap"></property>
</object>
</object>
<object class="wxMenu" expanded="1">
<property name="label">Reportes</property>
<property name="name">m_reports</property>
<property name="permission">protected</property>
<object class="wxMenuItem" expanded="1">
<property name="bitmap"></property>
<property name="checked">0</property>
<property name="enabled">1</property>
<property name="help"></property>
<property name="id">wxID_ANY</property>
<property name="kind">wxITEM_NORMAL</property>
<property name="label">Reporte de equipos</property>
<property name="name">srep_rcomp</property>
<property name="permission">none</property>
<property name="shortcut"></property>
<property name="unchecked_bitmap"></property>
</object>
<object class="wxMenuItem" expanded="1">
<property name="bitmap"></property>
<property name="checked">0</property>
<property name="enabled">1</property>
<property name="help"></property>
<property name="id">wxID_ANY</property>
<property name="kind">wxITEM_NORMAL</property>
<property name="label">Reporte de grupos</property>
<property name="name">srep_rgroup</property>
<property name="permission">none</property>
<property name="shortcut"></property>
<property name="unchecked_bitmap"></property>
</object>
<object class="wxMenuItem" expanded="1">
<property name="bitmap"></property>
<property name="checked">0</property>
<property name="enabled">1</property>
<property name="help"></property>
<property name="id">wxID_ANY</property>
<property name="kind">wxITEM_NORMAL</property>
<property name="label">Reporte de recetas</property>
<property name="name">srep_rcook</property>
<property name="permission">none</property>
<property name="shortcut"></property>
<property name="unchecked_bitmap"></property>
</object>
</object>
</object>
</object>
</object>
</wxFormBuilder_Project>
+218
View File
@@ -0,0 +1,218 @@
# Build 2
$root = $PSCommandPath | Split-Path -Parent
. $root\config.ps1
$computerName = $env:COMPUTERNAME
$UUID=(get-wmiobject Win32_ComputerSystemProduct).UUID
$exists = Invoke-RestMethod -Method Get -Uri "$server/get/computerexists?ComputerName=$computerName&UUID=$UUID"
if ($exists.Result -eq 0){
Write-Host "Computer outside database:" $computerName
exit
}
$Timestamp = [int64](([datetime]::UtcNow)-(get-date "1/1/1970")).TotalSeconds
# Hardware Data
$SOData = Get-CimInstance Win32_OperatingSystem
$SOVersion = $SOData.Version
if ($SOVersion -match "^10.0."){ #If its Windows 10, add revision number for knowing hotfix installed
$revi = -join(".",(Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion' UBR).UBR)
$SOVersion += $revi
}
# Update Computer Data
$CPUInfo = Get-WmiObject Win32_Processor
$RAMInstalled = Get-WmiObject CIM_PhysicalMemory | Measure-Object -Property capacity -Sum | ForEach-Object {[math]::round(($_.sum / 1MB),2)}
$paramInvoke = -join("$server/upd/computer?ComputerName=$computerName&UUID=",$UUID,"&SOVersion=",$SOVersion,"&SOCaption=",$SOData.Caption,"&SOBit=",$SOData.OsArchitecture.Substring(0,2),"&LastConnection=",$Timestamp,"&RAM=",$RAMInstalled,"&CPUName=",$CPUInfo.Name)
Invoke-RestMethod -Method Get -Uri $paramInvoke | out-null
# More info
$lastUser = (Get-CimInstance -ClassName Win32_ComputerSystem -Property UserName -ComputerName .).UserName
Invoke-RestMethod -Method Get -Uri "$server/upd/computer?ComputerName=$computerName&UUID=$UUID&LastUser=$lastUser" | out-null
$cooks = Invoke-RestMethod -Method Get -Uri "$server/get/cook?ComputerName=$computerName&UUID=$UUID"
foreach ($CookName in $cooks){
Set-Location $env:temp # For downloading/copying items, use system temp location
$CookName = $CookName.CookName
$cook = Invoke-RestMethod -Method Get -Uri "$server/load/cook?CookName=$CookName&ComputerName=$computerName&UUID=$UUID"
Write-Host "Receta:" $cook.name
$CookRevision = $cook.revision
$err = 0
$exit = 0
$inif = $false
$if = $true
$filesCopied = New-Object System.Collections.ArrayList # For REPOTOLOCAL, has a list of files copied to delete when finish (Do not store forever in temp)
foreach ($step in $cook.steps){
if ($err -eq 1 -and $noerror -eq 1){$err = 0; $errvar = ""} #If "noerror" is active, do not count errors
if ($err -eq 1 -or $exit -eq 1){break} # Halt if err ocurred (And noerror directive is not active)
$step = $step.Split("|")
$param = $step[1]
Write-Host $step[0] "-" $step[1]
if($inif -eq $true -and $if -eq $false){ # Only can see "ENDIF" if is in IF and is not true
if ($step[0] -ne "ENDIF" -and $step[0] -ne "ELSE"){
Write-Host $step[0] "Not executed, IF not meet"
$step[0] = "" #Disable command
$step[1] = ""
}
}
switch ($step[0].ToUpper()) { #Command
"UNINSTALL" { # Remove program
Get-Package -Name "$param*" -ErrorAction Continue #This will return error if program is not installed, do not see it.
if ($? -eq $true){ #If its True, is that package exists
Get-Package -Name "$param*" -ErrorVariable errvar | Uninstall-Package -ErrorVariable errvar
if ($? -eq $false){ # If fail then put 1 (When fail, powershell returns False)
$err = 1
}
}
}
"SERV_DISABLE" { # Disable a service
Set-Service $param -StartupType Disabled -Status Stopped -ErrorVariable errvar
if ($? -eq $false){ #If its False, it was a problem
$err = 1
}
}
"SERV_ENABLE" { # Enable a service
Set-Service $param -StartupType Automatic -Status Running -ErrorVariable errvar
if ($? -eq $false){ #If its False, it was a problem
$err = 1
}
}
"KILL_PROCESS" {
$p = Get-Process -Name "$param"
if ($? -eq $true){ # Only do something if exists
Stop-Process -InputObject $p -Force -ErrorVariable errvar -ErrorAction Continue
#if ($p.HasExited -eq $false){
# $err = 1
#}
}
}
"CMD" { # Run a cmd command. Note: Runs at high priv.
cmd.exe /c "$param"
if ($? -eq $false){ # Error in CMD
$err = 1
}
}
"PWCMD" { # Run a powershell command. Note: Runs as high priv.
Invoke-Expression $param
if ($? -eq $false){ # Error in CMD
$err = 1
}
}
"REPOTOLOCAL" { # Copy file from repo location to local ($env:temp) for use in cmd or other things
Copy-Item "$resources\$param" $env:temp -ErrorVariable errvar -Recurse -Force
if ($? -eq $false){ # Error in Copy
$err = 1
}
$filesCopied.Add($param) > $null #Add to list
}
"REMOVE" { # Remove files / folders
Remove-Item "$param" -Recurse -Force -ErrorAction Continue # They not see errors (Because error will be file not found)
}
"INSTALLMSI" { # Installs a .msi file (From $env:temp)
Start-Process msiexec.exe -Wait -ArgumentList "/norestart /quiet /I $param" -ErrorVariable errvar
if ($? -eq $false){ # Error in MSI
$err = 1
}
}
"REGFILE" { # Imports a .reg file
reg import .\$param
if ($? -eq $false){ # Error importing reg file
$err = 1
}
}
"MSG" { # Display a message
msg * "$param"
}
{$_ -in "SLEEP","PAUSE"}{ # Pause exec some seconds
[int]$secs = $param
Start-Sleep -Seconds $secs
}
"NOERROR" { #All within NOERROR doesn't generate errors and stop scripts
$noerror = 1
}
"ENDNOERROR" {
$noerror = 0
}
"IFSOFTWAREINST" { # If with software
$inif = $true #This controls IF start/stop
Get-Package -Name "$param*" -ErrorAction SilentlyContinue #This will return error if program is not installed, do not see it.
$if=$? # True -> Exists ; False -> Not exists
}
"IFSOFTWAREVER" { # If with software
$inif = $true #This controls IF start/stop
$parts = $param.Split(";")
if ($parts[1] -ne ""){ #Exists uri and filename
$p_name = $parts[0]
$p_ver = $parts[1]
Get-Package -Name "$p_name*" -MinimumVersion "$p_ver" -MaximumVersion "$p_ver" -ErrorAction SilentlyContinue #This will return error if program is not installed, do not see it.
$if=$? # True -> Exists ; False -> Not exists
}else{ #Doesn't sent right
$err = 1
$errvar = "Param not set right. Exiting..."
}
}
"IFPATHEXISTS" { # If only if a path exists (File, Folder, Registry Key..)
$inif = $true #This controls IF start/stop
Test-Path $param -PathType Any -ErrorAction SilentlyContinue
$if=$?
}
"IFPWCMD" { # If with powershell command
$inif = $true #This controls IF start/stop
Invoke-Expression $param #Executes powershell command
$if=$? # True -> Exists ; False -> Not exists
}
"ELSE" { # Turn bool $if
$if = !$if
}
"ENDIF"{ # End the if
$inif = $false
$if = $true
}
"DOWNLOAD" { #Download a file. This is a bit problematic one, will use ; to separate download and filename, and its forced to use it..
$parts = $param.Split(";")
if ($parts[1] -ne ""){ #Exists uri and filename
$progressPreference = 'silentlyContinue'
Invoke-WebRequest -Uri $parts[0] -OutFile $parts[1] -UseBasicParsing -ErrorVariable errvar
$progressPreference = 'Continue'
$filesCopied.Add($parts[1]) > $null #Add to list
}else{ #Doesn't sent right
$err = 1
$errvar = "Param not set right. Exiting..."
}
}
"EXIT"{ # Exits cook completly. If some param, exit will be with "error"
if ($param){ #Exit with error message
$noerror = 0
$err = 1
$errvar = $param
$exit = 1
}else{ #Exit as sucessful
$err = 0
$errvar = ""
$exit = 1
}
}
Default {}
}
}
# Send results
if ($errvar){ #There is an error if this has something
$errvar = $($errvar | Out-String)
$errvar = [System.Convert]::ToBase64String([System.Text.Encoding]::UNICODE.GetBytes($errvar))
$err = 1
}else{
$errvar=""
}
if ($cook.runever -eq "1"){
$err = -1 # This is for run but not for error, is by cook saw.
}
Invoke-RestMethod -Method Get -Uri "$server/set/cookstatus?ComputerName=$computerName&UUID=$UUID&CookName=$CookName&Revision=$CookRevision&Error=$err&ErrorDesc=$errvar" | out-null
#Delete files copied to temp for saving space
foreach ($element in $filesCopied) {
Remove-Item "$env:temp\$element" -ErrorAction SilentlyContinue -Recurse -Force
}
}
+2
View File
@@ -0,0 +1,2 @@
$server = "http://miserver.dominio:3333" #Server that runs api service
$resources = "\\MISERVER\REPOFOLDER" #Where files for copy are (Not cooks)
+317
View File
@@ -0,0 +1,317 @@
# Build 1
$root = $PSCommandPath | Split-Path -Parent
. $root\config.ps1
function seeGroups {
$answer = Invoke-RestMethod -Method Get -Uri "$server/get/groups"
foreach ($st in $answer){
Write-Host "Grupo:" $st.Name
}
}
function seeComputers {
$answer = Invoke-RestMethod -Method Get -Uri "$server/get/computers"
foreach ($st in $answer){
Write-Host "Ordenador:" $st.Name
}
}
function menuReportes {
do
{
Write-Host "================ Menu reportes ================"
Write-Host "1) Reporte de Software"
Write-Host "Q) Salir"
$input = Read-Host "Elegir una Opcion"
switch ($input)
{
'1' {
Clear-Host
. "$PSScriptRoot\ADcomputerData.ps1"
pause
} 'q' {
return
}
}
}
until ($input -eq 'q')
}
function menuEquipos {
do
{
Write-Host "================ Menu equipos ================"
Write-Host "1) Reporte de Equipos en pantalla"
Write-Host "2) Añadir equipo"
Write-Host "3) Borrar equipo"
Write-Host "4) Añadir equipo a un grupo"
Write-Host "5) Borrar equipo de un grupo"
Write-Host "Q) Salir"
$input = Read-Host "Elegir una Opcion"
switch ($input)
{
'1' {
Clear-Host
seeComputers #Function
pause
} '2' {
Clear-Host
$newHost = Read-Host -Prompt "Nombre del equipo"
if (Test-Connection -ComputerName $newHost -Quiet){
Write-Output "$newHost Online"
Write-Output "Añadiendo equipo"
Invoke-RestMethod -Method Get -Uri "$server/add/computer?ComputerName=$newHost"
}else{
Write-Output "$newHost Offline"
}
pause
} '3' {
Clear-Host
$hhost = Read-Host -Prompt "Nombre del equipo a eliminar"
Invoke-RestMethod -Method Get -Uri "$server/del/computer?ComputerName=$hhost"
} '4' {
Clear-Host
$computer = Read-Host -Prompt "Nombre del equipo"
# See all groups
seeGroups #Function
$newGroup = Read-Host -Prompt "Nombre del grupo a añadir"
Invoke-RestMethod -Method Get -Uri "$server/add/grpcomputer?ComputerName=$computer&GroupName=$newGroup"
pause
} '5' {
Clear-Host
$computer = Read-Host -Prompt "Nombre del equipo"
# See all groups
seeGroups #Function
$newGroup = Read-Host -Prompt "Nombre del grupo a quitar"
Invoke-RestMethod -Method Get -Uri "$server/add/grpcomputer?ComputerName=$computer&GroupName=$newGroup"
pause
} 'q' {
return
}
}
}
until ($input -eq 'q')
}
function menuGrupos {
do
{
Write-Host "================ Menu grupos ================"
Write-Host "1) Reporte de Grupos en pantalla"
Write-Host "2) Ver equipos de un grupo"
Write-Host "3) Añadir grupo"
Write-Host "4) Borrar grupo"
Write-Host "5) Vaciar equipos del grupo"
Write-Host "6) Cambiar nombre del grupo"
Write-Host "7) Ver recetas de un grupo"
Write-Host "Q) Salir"
$input = Read-Host "Elegir una Opción"
switch ($input)
{
'1' {
Clear-Host
seeGroups #Function
pause
} '2' {
Clear-Host
seeGroups
$GroupName = Read-Host -Prompt "Nombre del grupo a ver"
if ($GroupName){
$answer = Invoke-RestMethod -Method Get -Uri "$server/get/computersgrp?GroupName=$GroupName"
foreach ($st in $answer){
Write-Host "Ordenador:" $st.Name
}
}
pause
} '3' {
Clear-Host
$newGroup = Read-Host -Prompt "Nombre del grupo a añadir"
Invoke-RestMethod -Method Get -Uri "$server/add/group?GroupName=$newGroup"
pause
} '4' {
Clear-Host
seeGroups #Function
$grpName = Read-Host -Prompt "Nombre del grupo a eliminar"
Invoke-RestMethod -Method Get -Uri "$server/del/group?GroupName=$grpName"
} '5' {
Clear-Host
seeGroups #Function
$grpName = Read-Host -Prompt "Nombre del grupo a quitar los equipos"
Invoke-RestMethod -Method Get -Uri "$server/del/emptypcsgroup?GroupName=$grpName"
} '6' {
Clear-Host
# See all groups
seeGroups #Function
$GroupName = Read-Host -Prompt "Nombre del grupo"
$GroupNewName = Read-Host -Prompt "Nombre nuevo"
Invoke-RestMethod -Method Get -Uri "$server/upd/group?GroupName=$GroupName&GroupNewName=$GroupNewName"
pause
} '7' {
Clear-Host
# See all groups
seeGroups #Function
$GroupName = Read-Host -Prompt "Nombre del grupo para ver recetas"
if ($GroupName){
$answer = Invoke-RestMethod -Method Get -Uri "$server/get/cookgrp?GroupName=$GroupName"
foreach ($st in $answer){
Write-Host "Receta:" $st.CookName
}
}else{
Write-Host "No hay grupo"
}
pause
} 'q' {
return
}
}
}
until ($input -eq 'q')
}
function menuCooks {
do
{
Write-Host "================ Menu recetas (Cooks) ================"
Write-Host "1) Grupos de una receta"
Write-Host "2) Estado de una receta"
Write-Host "3) Detalles de cada equipo de una receta"
Write-Host "4) Añadir receta a un grupo"
Write-Host "5) Borrar receta de un grupo"
Write-Host "6) Renombrar receta"
Write-Host "Q) Salir"
$input = Read-Host "Elegir una Opción"
switch -regex ($input)
{
'1' {
Clear-Host
$CookName = Read-Host -Prompt "Nombre de la receta"
Write-Host "Grupos de la receta $CookName"
$grpcook = Invoke-RestMethod -Method Get -Uri "$server/get/grpcook?CookName=$CookName"
foreach ($st in $grpcook){
Write-Host "Grupo:" $st.Name
}
pause
} '2|3' {
Clear-Host
$CookName = Read-Host -Prompt "Nombre de la receta"
Write-Host "Grupos de la receta"
$grpcook = Invoke-RestMethod -Method Get -Uri "$server/get/grpcook?CookName=$CookName"
foreach ($st in $grpcook){
Write-Host "Grupo:" $st.Name
}
$cookrevision = Invoke-RestMethod -Method Get -Uri "$server/get/lastrevisioncook?CookName=$CookName"
$answer = Invoke-RestMethod -Method Get -Uri "$server/get/statuscook?CookName=$CookName"
if ($_ -eq '3'){
foreach ($st in $answer){
Write-Host "Ordenador:" $st.Name ", revision" $st.Revision ", error" $st.Error
}
Write-Host "------Resumen y errores------"
}
$estadistica = @{}
$estadistica.Total = 0
$estadistica.Error = 0
$estadistica.NotLast = 0
foreach ($st in $answer){
$estadistica.Total++
if ($st.Error -eq '1'){
Write-Host "Ordenador en error:" $st.Name
$estadistica.Error++
}
if ($st.Revision -ne $cookrevision.Revision){
Write-Host "Ordenador sin la última revisión de la receta:" $st.Name
$estadistica.NotLast++
}
}
Write-Host "Total implementado:" $estadistica.Total
Write-Host "Errores:" $estadistica.Error
Write-Host "Sin aplicar ultima revisión:" $estadistica.NotLast
pause
} '4' {
Clear-Host
$CookName = Read-Host -Prompt "Nombre de la receta a añadir"
seeGroups #Function
$GroupName = Read-Host -Prompt "Nombre del grupo que quieres que lo tenga"
Invoke-RestMethod -Method Get -Uri "$server/set/cook?GroupName=$GroupName&CookName=$CookName"
pause
} '5' {
Clear-Host
$CookName = Read-Host -Prompt "Nombre del receta a eliminar de un grupo"
$grpcook = Invoke-RestMethod -Method Get -Uri "$server/get/grpcook?CookName=$CookName"
foreach ($st in $grpcook){
Write-Host "Grupo:" $st.Name
}
$GroupName = Read-Host -Prompt "Nombre del grupo"
Invoke-RestMethod -Method Get -Uri "$server/del/cookgrp?GroupName=$GroupName&CookName=$CookName"
pause
} '6' {
Clear-Host
$CookName = Read-Host -Prompt "Nombre del receta actual"
$CookNewName = Read-Host -Prompt "Nuevo nombre"
Invoke-RestMethod -Method Get -Uri "$server/upd/cookname?CookNewName=$CookNewName&CookName=$CookName"
pause
} 'q' {
return
}
}
}
until ($input -eq 'q')
}
do
{
Write-Host "================ Menú principal ================"
Write-Host "1) Reportes"
Write-Host "2) Equipos"
Write-Host "3) Grupos (Locales)"
Write-Host "4) Recetas"
Write-Host "Q) Salir"
$input = Read-Host "Elegir una Opción"
switch ($input)
{
'1' {
Clear-Host
menuReportes
} '2' {
Clear-Host
menuEquipos
} '3' {
Clear-Host
menuGrupos
} '4' {
Clear-Host
menuCooks
} 'q' {
return
}
}
}
until ($input -eq 'q')
+1
View File
@@ -0,0 +1 @@
# Here you have to put cook files in .yaml
+5
View File
@@ -0,0 +1,5 @@
name: Install exe file, example with WinSCP
revision: 1
steps:
- REPOTOLOCAL|WinSCP-5.13.4-Setup.exe
- CMD|WinSCP-5.13.4-Setup.exe /VERYSILENT /NORESTART
@@ -0,0 +1,6 @@
name: Install MSI with options (Do not put standard silent options!)
revision: 2
steps:
- REPOTOLOCAL|msiwithoptions.msi
- INSTALLMSI|msiwithoptions.msi ADDLOCAL=Server SERVER_REGISTER_AS_SERVICE=1 SERVER_ADD_FIREWALL_EXCEPTION=1 SERVER_ALLOW_SAS=1
- REMOVE|C:\ProgramData\Microsoft\Windows\Start Menu\Programs\ProgramThatIDoNotWantUserToSee|Delete start menu folder
+5
View File
@@ -0,0 +1,5 @@
name: Install MSI File. By default, it is silent
revision: 1
steps:
- REPOTOLOCAL|simplemsi.msi
- INSTALLMSI|simplemsi.msi
+7
View File
@@ -0,0 +1,7 @@
name: Remove WinRar as Example
revision: 4
steps:
- UNINSTALL|WinRAR|This uninstalls WinRAR from Control Panel directly (Powershell powered uninstall)
- NOERROR|Do not fail if this fails
- CMD|"C:\Program Files\WinRAR\Uninstall.exe" /S
- ENDNOERROR|End of never exit if fail
+24
View File
@@ -0,0 +1,24 @@
# File hierarchy
## Root folder
- api.py -> Server file. It manages all requests from clients and connects to database
- loadserver.bat -> A helper to run api.py on Windows
- sql.py -> Helper for api.py sql sentences
- sysopt.py -> Config of api and sql
## Client folder
- client.ps1 -> This file has to run every X time (I suggest every hour) on every client and as NT\SYSTEM or local admin. You can do this with a Task Schedule as SYSTEM in a GPO (Active Directory)
- control.ps1 -> This is the cli of the program. With that you manages the client-server program. It has to be in same folder as client.ps1 file
## BD folder
- database.db -> Database file in sqlite3
## cooks folder
It has the cooks in yaml format.
Warning: Do not change name of cooks created and assigned directly.
## Doc folder
Documentation...
## REPO folder (This isn't needeed to be there)
You have to have this shared on LAN, this is the folder that cooks use to retrieve your files (As .exe/.msi) to use in scripts.
+13
View File
@@ -0,0 +1,13 @@
# Very-Fast instructions
You have to create a cook
In "Examples" you have Cook Examples
Then, you have to create a group using the CLI.
In the groups is where you assign computers and cooks
Cooks -> Group
Computers -> Group
You can have more than 1 cook in a group
You can call the groups equal to cooks and assign this cook to the group and you have a 1<->1
+48
View File
@@ -0,0 +1,48 @@
# How to
Cooks are in yaml format, and have this structure.
First: Name, a line like below, with the name
"name: My Super Cook"
Then, the revision number, when this number changes, cook will be reapplied
"revision: 1"
If the cook has to run everytime client is called (And not only one time per new revision), you have to add:
"runever: 1" (1-> Everytime; 0 or no line: Run only one time)
And then, steps. First a "steps:" line, and then line by line the steps of the cook
"steps:
- PWCMD|White-Output "Hello World"
"
Steps are ever put like "Type of step" | "Command"
You can put comments in every line putting another "|" and comments at finish, like.. "CMD|WinSCP-5.13.4-Setup.exe /VERYSILENT /NORESTART|Note: Installing WinSCP"
Cook finishes executing if they had and error (Except in a NOERROR block), or if finishes right.
Note: Cooks start in directory $env:temp (%TEMP%) from SYSTEM user.
# Type of commands
- CMD and PWCMD: cmd command or Powershell command. Cmd commands can not work properly if has quotes (Because its run as cmd /c ""), if they are a Powershell equivalent, please use it. Normally cmd command uses are for install/uninstall exe programs or some like that, because powershell doesn't wait to exit a exe command
- REPOTOLOCAL: Copy a file from REPO folder configured to local in temp folder to use it. You can copy files or folders with same command.
- INSTALLMSI: Installs an MSI file silently (Params /quiet /norestart are put, do not repeat). You can add parameters (Like INSTALLMSI|superapp.msi ADDLOCAL=extension SETASDEFAULT=1)
- NOERROR and ENDNOERROR: This is for creating a block of instructions that not captures errors, then cook works ever if instruction fails
Example:
NOERROR|For deleting a file that maybe can not exist
PWCMD|Remove-Item C:\Windows\loginstall.txt
ENDNOERROR|Finish
- SERV_DISABLE and SERV_ENABLE: Enables or disables a Windows Service
- MSG|Display a message to user
- COMMENT or REM: Makes cook comments (COMMENT|This is a comment)
- KILL_PROCESS: Kill a process name if exists
- UNINSTALL: Uninstalls a program by name if its installed. It searchs it by Powershell command 'Get-Package -Name "Program*"', take care of it. You can use to see name
- REMOVE: Removes a file or folder path from local computer. It's recursive
- SLEEP and PAUSE: Pauses execution seconds specified in param
- DOWNLOAD: This downloads a file from network/internet to $env:temp/filename. Its *required* to put a filename, that goes this way: DOWNLOAD|http://www.example.org/file.zip;filename.zip. ";" is that separates URL and filename. It seems to be a limit in PowerShell Invoke-WebRequest that limits this downloads to some like 128MB
- EXIT: Terminates a cook in this instruction. If a param is specified, exit will be with error code and the param as text of error. (Ej: EXIT|Error at seeking for file)
## If types
Inside a cook, you can use IF/ELSE/ENDIF scheme.
- IFSOFTWAREINST: Runs the if, if a program is installed.
- IFSOFTWAREVER: Runs the if, if a program is installed and has X version. (Run with two param: "Name;Version")
- IFPATHEXISTS: Runs if file exists
- IFPWCMD: If powershell command returns $true (Executed succesfully), if is run
- ELSE: Else..
- ENDIF
+3
View File
@@ -0,0 +1,3 @@
Cooks have to be only alfanumeric caracters and "-","_" and must end with .yaml
Groups have to be alfanumeric and "-","_"
+3
View File
@@ -0,0 +1,3 @@
REM Starting Server. Will only work if python and dependencies are installed and in PATH
cd /D "%~dp0"
waitress-serve --port=3333 api:api
+3
View File
@@ -0,0 +1,3 @@
#!/bin/bash
cd "$(dirname "$(realpath "$0")")";
waitress-serve --port=3333 api:api
+45
View File
@@ -0,0 +1,45 @@
import sqlite3
import sysopt #File of options
from shutil import copyfile
from os import path
from datetime import datetime
if not path.isfile('BD/database.db'):
copyfile('BD/emptydatabase.db','BD/database.db')
def logit(text):
text = str(text)
now = datetime.now()
print ("Log: " + text)
with open('api.log', 'a') as file:
file.write("Log ("+now.strftime("%x %X") + "): ")
file.write(text)
file.write('\n')
def dict_factory(cursor, row):
d = {}
for idx, col in enumerate(cursor.description):
d[col[0]] = row[idx]
return d
def select(query):
conn = sqlite3.connect('BD/database.db')
conn.row_factory = dict_factory
if sysopt.debugsql==True:
logit("SQL: "+query)
cur = conn.execute(query)
return cur.fetchall()
def insert(query):
conn = sqlite3.connect('BD/database.db')
if sysopt.debugsql==True:
logit(query)
try:
c = conn.cursor()
c.execute("SQL: "+query)
conn.commit()
conn.close()
return {'RESULT': 'OK'}
except:
return {'RESULT': 'SQLite3 Error'}
+3
View File
@@ -0,0 +1,3 @@
addComputers = False # False/True accept auto add computers that start client program and connect to server
debugsql = True # True is show in log all SQL queries