paramsx 2.2.2__tar.gz → 2.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {paramsx-2.2.2 → paramsx-2.3.0}/CHANGELOG.md +16 -0
- {paramsx-2.2.2/paramsx.egg-info → paramsx-2.3.0}/PKG-INFO +46 -2
- {paramsx-2.2.2 → paramsx-2.3.0}/README.md +45 -1
- paramsx-2.3.0/paramsx/__init__.py +1 -0
- {paramsx-2.2.2 → paramsx-2.3.0}/paramsx/funcions.py +109 -3
- {paramsx-2.2.2 → paramsx-2.3.0}/paramsx/main.py +76 -24
- {paramsx-2.2.2 → paramsx-2.3.0}/paramsx/paramsx_config.py +10 -0
- {paramsx-2.2.2 → paramsx-2.3.0/paramsx.egg-info}/PKG-INFO +46 -2
- paramsx-2.2.2/paramsx/__init__.py +0 -1
- {paramsx-2.2.2 → paramsx-2.3.0}/LICENSE +0 -0
- {paramsx-2.2.2 → paramsx-2.3.0}/MANIFEST.in +0 -0
- {paramsx-2.2.2 → paramsx-2.3.0}/paramsx/text.py +0 -0
- {paramsx-2.2.2 → paramsx-2.3.0}/paramsx.egg-info/SOURCES.txt +0 -0
- {paramsx-2.2.2 → paramsx-2.3.0}/paramsx.egg-info/dependency_links.txt +0 -0
- {paramsx-2.2.2 → paramsx-2.3.0}/paramsx.egg-info/entry_points.txt +0 -0
- {paramsx-2.2.2 → paramsx-2.3.0}/paramsx.egg-info/requires.txt +0 -0
- {paramsx-2.2.2 → paramsx-2.3.0}/paramsx.egg-info/top_level.txt +0 -0
- {paramsx-2.2.2 → paramsx-2.3.0}/setup.cfg +0 -0
- {paramsx-2.2.2 → paramsx-2.3.0}/setup.py +0 -0
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.3.0
|
|
4
|
+
|
|
5
|
+
### Añadido
|
|
6
|
+
|
|
7
|
+
- **La descripción de los parámetros, editable como el valor.** El fichero exportado trae un campo `parameter_description` **debajo del nombre y antes del valor**: se lee de AWS, lo editas y al cargar se actualiza. Los cambios de descripción se detectan igual que los de valor y aparecen en la pantalla de confirmación como `Modificado (descripción)`. La opción 4 también la pide al crear un parámetro, entre la ruta y el valor.
|
|
8
|
+
- Los backups incluyen la descripción, así que un backup restaura también las descripciones.
|
|
9
|
+
- **Flag `forzar_securestring`** (por defecto `True`, el comportamiento de siempre): todo lo que sube ParamsX va cifrado como `SecureString`. Con `False` se respeta el tipo que ya tuviera el parámetro en AWS: al actualizar no se manda el tipo y AWS conserva el suyo (`Type` solo es obligatorio al crear). El fichero exportado trae además un campo `parameter_type` editable, por si quieres cambiar el tipo a propósito, y sus cambios se detectan como los del valor o la descripción. El tipo se lee de la misma llamada que el valor, así que no hace falta ningún permiso nuevo.
|
|
10
|
+
|
|
11
|
+
### Nota sobre el valor y la descripción
|
|
12
|
+
|
|
13
|
+
`put_parameter` con `Overwrite=True` reescribe la definición del parámetro, así que ParamsX **manda siempre la descripción que trae el fichero**, incluso cuando lo único que cambió fue el valor. Antes no se mandaba, y una descripción que ya existiera en AWS podía quedarse vacía al editar el valor.
|
|
14
|
+
|
|
15
|
+
### Nota sobre permisos
|
|
16
|
+
|
|
17
|
+
Leer las descripciones necesita **`ssm:DescribeParameters`**, porque no vienen en `GetParametersByPath` (son metadatos, no valores). Si tu rol no lo tiene, ParamsX te avisa al leer, el campo no aparece en el fichero y al cargar **no se toca** la descripción que haya en AWS. El resto sigue funcionando igual.
|
|
18
|
+
|
|
3
19
|
## 2.2.2
|
|
4
20
|
|
|
5
21
|
Un renombrado de la configuración, sin cambios de comportamiento. **Los nombres antiguos
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: paramsx
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: Librería para gestionar y respaldar parámetros de AWS SSM de manera sencilla y eficiente.
|
|
5
5
|
Home-page: https://github.com/pistatxos/paramsx
|
|
6
6
|
Author: Mariox
|
|
@@ -70,7 +70,7 @@ Al ejecutar ```paramsx``` desde la terminal, accedes a un menú interactivo con
|
|
|
70
70
|
- Navega por la lista de parámetros configurada en tu archivo de configuración y selecciona cuál descargar.
|
|
71
71
|
- Elige el entorno deseado (por ejemplo, dev o prod).
|
|
72
72
|
- Archivos generados:
|
|
73
|
-
- parameters_dev.py o parameters_prod.py → Edita estos archivos para modificar, añadir o eliminar parámetros (valores y tags).
|
|
73
|
+
- parameters_dev.py o parameters_prod.py → Edita estos archivos para modificar, añadir o eliminar parámetros (descripciones, valores y tags).
|
|
74
74
|
- parameters_dev_backup.py → Respaldo automático del archivo descargado, incluidas las tags.
|
|
75
75
|
Los archivos se crean en la misma ruta desde donde ejecutas paramsx, evitando movernos innecesariamente entre carpetas.
|
|
76
76
|
- Si tu rol IAM no tiene permisos sobre la ruta configurada, verás un mensaje claro en lugar de una traza de boto3:
|
|
@@ -98,6 +98,7 @@ Esto te permitirá mantener respaldos seguros o realizar migraciones/reorganizac
|
|
|
98
98
|
4. Crear nuevo parámetro
|
|
99
99
|
- Crea un parámetro desde cero sin salir de la herramienta. Te pedirá, en este orden:
|
|
100
100
|
- La ruta **sin el entorno**, igual que la declaras en `parameter_list`: el perfil se encarga de colocarlo. Verás en pantalla la ruta resultante en AWS antes de seguir.
|
|
101
|
+
- La descripción, opcional: para qué sirve el parámetro. Se guarda en AWS y luego se puede editar en el fichero exportado.
|
|
101
102
|
- El valor: texto plano o JSON en una línea (por ejemplo `{"host": "x", "user": "y", "pass": "z"}`), que se valida antes de continuar.
|
|
102
103
|
- Las tags obligatorias, si tienes `tags_activas = True`.
|
|
103
104
|
- El perfil que se usa es el que declares en `perfil_nuevos`. Si tu perfil es de tipo `mixto`, escribe el `*` en la ruta para indicar dónde va el entorno.
|
|
@@ -212,6 +213,16 @@ perfil_nuevos = "min"
|
|
|
212
213
|
# segunda lectura te machaque el fichero de la primera.
|
|
213
214
|
fichero_por_ruta = False
|
|
214
215
|
|
|
216
|
+
# ¿Todo lo que sube ParamsX va cifrado como SecureString?
|
|
217
|
+
# True (por defecto) -> sí, siempre. Es lo recomendable: los parámetros de una cuenta
|
|
218
|
+
# suelen tener secretos, y así no depende de acordarse.
|
|
219
|
+
# False -> se respeta el tipo que ya tuviera el parámetro en AWS (String, StringList o
|
|
220
|
+
# SecureString): al actualizar no se manda el tipo, así que AWS conserva el suyo.
|
|
221
|
+
# En este modo el fichero exportado trae además un campo 'parameter_type' que
|
|
222
|
+
# puedes editar si quieres cambiarlo a propósito. Los parámetros que crees con
|
|
223
|
+
# la opción 4 se siguen creando como SecureString.
|
|
224
|
+
forzar_securestring = True
|
|
225
|
+
|
|
215
226
|
|
|
216
227
|
## Configuraciones Tags
|
|
217
228
|
|
|
@@ -269,6 +280,38 @@ En el nombre van el entorno, la ruta y el perfil, porque dos entradas pueden com
|
|
|
269
280
|
| `{"path": "/API/STA", "perfil": "max"}` | `parameters_dev__API_STA__max.py` |
|
|
270
281
|
| `{"path": "/API/*/STA", "perfil": "mixto_max"}` | `parameters_dev__API_env_STA__mixto_max.py` |
|
|
271
282
|
|
|
283
|
+
### La descripción de los parámetros
|
|
284
|
+
|
|
285
|
+
El fichero exportado trae un campo `parameter_description` **debajo del nombre y antes del valor**. Se lee de AWS, se edita como cualquier otro campo y se aplica al cargar:
|
|
286
|
+
|
|
287
|
+
```python
|
|
288
|
+
parametros = [
|
|
289
|
+
{'parameter_name': '/dev/api/sta/token',
|
|
290
|
+
'parameter_description': "Token JWT de la API, rota cada 90 dias",
|
|
291
|
+
'parameter_value': """abc123"""},
|
|
292
|
+
]
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Dos cosas que conviene saber:
|
|
296
|
+
|
|
297
|
+
- **Hace falta el permiso `ssm:DescribeParameters`.** La descripción no viaja en `GetParametersByPath` como el valor: es un metadato y se pide con `DescribeParameters`. Si tu rol no lo tiene, ParamsX te avisa, el campo **no aparece** en el fichero y al cargar no se toca la descripción que haya en AWS.
|
|
298
|
+
- **Al actualizar un parámetro, AWS reescribe su definición entera.** Por eso ParamsX manda siempre la descripción del fichero cuando cambia el valor, aunque no la hayas tocado: si no la mandara, se perdería. Y como no existe una API para cambiar solo la descripción, editarla crea una versión nueva del parámetro igual que cambiar el valor.
|
|
299
|
+
|
|
300
|
+
Un cambio de descripción se ve en la pantalla de confirmación como `Modificado (descripción)`, y si cambias las dos cosas, `Modificado (valor | descripción)`.
|
|
301
|
+
|
|
302
|
+
### Cifrado: `forzar_securestring`
|
|
303
|
+
|
|
304
|
+
ParamsX sube todo como **SecureString** por defecto, y así se recomienda dejarlo: los parámetros de una cuenta acaban teniendo secretos, y con esto no depende de que nadie se acuerde.
|
|
305
|
+
|
|
306
|
+
| `forzar_securestring` | Qué hace |
|
|
307
|
+
|---|---|
|
|
308
|
+
| `True` (por defecto) | Todo lo que se sube va cifrado como `SecureString`, sea del tipo que sea en AWS. |
|
|
309
|
+
| `False` | Se respeta el tipo que ya tuviera el parámetro (`String`, `StringList` o `SecureString`): al actualizar no se manda el tipo, y AWS conserva el suyo. El fichero exportado trae además un campo `parameter_type` que puedes editar si quieres cambiarlo a propósito, y ese cambio cuenta como uno más: verás `Modificado (tipo: SecureString)`. |
|
|
310
|
+
|
|
311
|
+
Los parámetros que creas con la opción 4 se crean siempre como `SecureString`, en los dos modos.
|
|
312
|
+
|
|
313
|
+
El tipo se lee de la misma llamada que el valor, así que **no hace falta ningún permiso extra** para esto (a diferencia de la descripción).
|
|
314
|
+
|
|
272
315
|
### Cómo acotar tu parameter_list (recomendación)
|
|
273
316
|
|
|
274
317
|
`parameter_list` es solo tu vista de trabajo: **la seguridad real la impone IAM, no este fichero**. La recomendación de uso es:
|
|
@@ -300,6 +343,7 @@ Formato del fichero exportado con `tags_activas = True`:
|
|
|
300
343
|
```python
|
|
301
344
|
parametros = [
|
|
302
345
|
{'parameter_name': '/dev/email/users',
|
|
346
|
+
'parameter_description': "Buzon que recibe los avisos de altas",
|
|
303
347
|
'parameter_value': """agpetrovici@stanalytics.es""",
|
|
304
348
|
'tagApplication': "",
|
|
305
349
|
'tagEnvironment': "",
|
|
@@ -50,7 +50,7 @@ Al ejecutar ```paramsx``` desde la terminal, accedes a un menú interactivo con
|
|
|
50
50
|
- Navega por la lista de parámetros configurada en tu archivo de configuración y selecciona cuál descargar.
|
|
51
51
|
- Elige el entorno deseado (por ejemplo, dev o prod).
|
|
52
52
|
- Archivos generados:
|
|
53
|
-
- parameters_dev.py o parameters_prod.py → Edita estos archivos para modificar, añadir o eliminar parámetros (valores y tags).
|
|
53
|
+
- parameters_dev.py o parameters_prod.py → Edita estos archivos para modificar, añadir o eliminar parámetros (descripciones, valores y tags).
|
|
54
54
|
- parameters_dev_backup.py → Respaldo automático del archivo descargado, incluidas las tags.
|
|
55
55
|
Los archivos se crean en la misma ruta desde donde ejecutas paramsx, evitando movernos innecesariamente entre carpetas.
|
|
56
56
|
- Si tu rol IAM no tiene permisos sobre la ruta configurada, verás un mensaje claro en lugar de una traza de boto3:
|
|
@@ -78,6 +78,7 @@ Esto te permitirá mantener respaldos seguros o realizar migraciones/reorganizac
|
|
|
78
78
|
4. Crear nuevo parámetro
|
|
79
79
|
- Crea un parámetro desde cero sin salir de la herramienta. Te pedirá, en este orden:
|
|
80
80
|
- La ruta **sin el entorno**, igual que la declaras en `parameter_list`: el perfil se encarga de colocarlo. Verás en pantalla la ruta resultante en AWS antes de seguir.
|
|
81
|
+
- La descripción, opcional: para qué sirve el parámetro. Se guarda en AWS y luego se puede editar en el fichero exportado.
|
|
81
82
|
- El valor: texto plano o JSON en una línea (por ejemplo `{"host": "x", "user": "y", "pass": "z"}`), que se valida antes de continuar.
|
|
82
83
|
- Las tags obligatorias, si tienes `tags_activas = True`.
|
|
83
84
|
- El perfil que se usa es el que declares en `perfil_nuevos`. Si tu perfil es de tipo `mixto`, escribe el `*` en la ruta para indicar dónde va el entorno.
|
|
@@ -192,6 +193,16 @@ perfil_nuevos = "min"
|
|
|
192
193
|
# segunda lectura te machaque el fichero de la primera.
|
|
193
194
|
fichero_por_ruta = False
|
|
194
195
|
|
|
196
|
+
# ¿Todo lo que sube ParamsX va cifrado como SecureString?
|
|
197
|
+
# True (por defecto) -> sí, siempre. Es lo recomendable: los parámetros de una cuenta
|
|
198
|
+
# suelen tener secretos, y así no depende de acordarse.
|
|
199
|
+
# False -> se respeta el tipo que ya tuviera el parámetro en AWS (String, StringList o
|
|
200
|
+
# SecureString): al actualizar no se manda el tipo, así que AWS conserva el suyo.
|
|
201
|
+
# En este modo el fichero exportado trae además un campo 'parameter_type' que
|
|
202
|
+
# puedes editar si quieres cambiarlo a propósito. Los parámetros que crees con
|
|
203
|
+
# la opción 4 se siguen creando como SecureString.
|
|
204
|
+
forzar_securestring = True
|
|
205
|
+
|
|
195
206
|
|
|
196
207
|
## Configuraciones Tags
|
|
197
208
|
|
|
@@ -249,6 +260,38 @@ En el nombre van el entorno, la ruta y el perfil, porque dos entradas pueden com
|
|
|
249
260
|
| `{"path": "/API/STA", "perfil": "max"}` | `parameters_dev__API_STA__max.py` |
|
|
250
261
|
| `{"path": "/API/*/STA", "perfil": "mixto_max"}` | `parameters_dev__API_env_STA__mixto_max.py` |
|
|
251
262
|
|
|
263
|
+
### La descripción de los parámetros
|
|
264
|
+
|
|
265
|
+
El fichero exportado trae un campo `parameter_description` **debajo del nombre y antes del valor**. Se lee de AWS, se edita como cualquier otro campo y se aplica al cargar:
|
|
266
|
+
|
|
267
|
+
```python
|
|
268
|
+
parametros = [
|
|
269
|
+
{'parameter_name': '/dev/api/sta/token',
|
|
270
|
+
'parameter_description': "Token JWT de la API, rota cada 90 dias",
|
|
271
|
+
'parameter_value': """abc123"""},
|
|
272
|
+
]
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Dos cosas que conviene saber:
|
|
276
|
+
|
|
277
|
+
- **Hace falta el permiso `ssm:DescribeParameters`.** La descripción no viaja en `GetParametersByPath` como el valor: es un metadato y se pide con `DescribeParameters`. Si tu rol no lo tiene, ParamsX te avisa, el campo **no aparece** en el fichero y al cargar no se toca la descripción que haya en AWS.
|
|
278
|
+
- **Al actualizar un parámetro, AWS reescribe su definición entera.** Por eso ParamsX manda siempre la descripción del fichero cuando cambia el valor, aunque no la hayas tocado: si no la mandara, se perdería. Y como no existe una API para cambiar solo la descripción, editarla crea una versión nueva del parámetro igual que cambiar el valor.
|
|
279
|
+
|
|
280
|
+
Un cambio de descripción se ve en la pantalla de confirmación como `Modificado (descripción)`, y si cambias las dos cosas, `Modificado (valor | descripción)`.
|
|
281
|
+
|
|
282
|
+
### Cifrado: `forzar_securestring`
|
|
283
|
+
|
|
284
|
+
ParamsX sube todo como **SecureString** por defecto, y así se recomienda dejarlo: los parámetros de una cuenta acaban teniendo secretos, y con esto no depende de que nadie se acuerde.
|
|
285
|
+
|
|
286
|
+
| `forzar_securestring` | Qué hace |
|
|
287
|
+
|---|---|
|
|
288
|
+
| `True` (por defecto) | Todo lo que se sube va cifrado como `SecureString`, sea del tipo que sea en AWS. |
|
|
289
|
+
| `False` | Se respeta el tipo que ya tuviera el parámetro (`String`, `StringList` o `SecureString`): al actualizar no se manda el tipo, y AWS conserva el suyo. El fichero exportado trae además un campo `parameter_type` que puedes editar si quieres cambiarlo a propósito, y ese cambio cuenta como uno más: verás `Modificado (tipo: SecureString)`. |
|
|
290
|
+
|
|
291
|
+
Los parámetros que creas con la opción 4 se crean siempre como `SecureString`, en los dos modos.
|
|
292
|
+
|
|
293
|
+
El tipo se lee de la misma llamada que el valor, así que **no hace falta ningún permiso extra** para esto (a diferencia de la descripción).
|
|
294
|
+
|
|
252
295
|
### Cómo acotar tu parameter_list (recomendación)
|
|
253
296
|
|
|
254
297
|
`parameter_list` es solo tu vista de trabajo: **la seguridad real la impone IAM, no este fichero**. La recomendación de uso es:
|
|
@@ -280,6 +323,7 @@ Formato del fichero exportado con `tags_activas = True`:
|
|
|
280
323
|
```python
|
|
281
324
|
parametros = [
|
|
282
325
|
{'parameter_name': '/dev/email/users',
|
|
326
|
+
'parameter_description': "Buzon que recibe los avisos de altas",
|
|
283
327
|
'parameter_value': """agpetrovici@stanalytics.es""",
|
|
284
328
|
'tagApplication': "",
|
|
285
329
|
'tagEnvironment': "",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.3.0"
|
|
@@ -161,7 +161,10 @@ def get_parameters_by_prefix(ssm, prefix):
|
|
|
161
161
|
for param in response.get('Parameters', []):
|
|
162
162
|
parameters.append({
|
|
163
163
|
"parameter_name": param['Name'],
|
|
164
|
-
"parameter_value": param['Value']
|
|
164
|
+
"parameter_value": param['Value'],
|
|
165
|
+
# El tipo viene en la misma respuesta que el valor, sin permisos extra.
|
|
166
|
+
# Solo se usa si forzar_securestring = False.
|
|
167
|
+
"parameter_type": param.get('Type', "SecureString"),
|
|
165
168
|
})
|
|
166
169
|
|
|
167
170
|
next_token = response.get('NextToken')
|
|
@@ -174,6 +177,61 @@ def get_parameters_by_prefix(ssm, prefix):
|
|
|
174
177
|
return parameters
|
|
175
178
|
|
|
176
179
|
|
|
180
|
+
# Descripciones de los parámetros de una ruta, indexadas por nombre.
|
|
181
|
+
# La descripción NO viene en get_parameters_by_path: hay que pedirla aparte con
|
|
182
|
+
# describe_parameters, que es la que devuelve los metadatos. Eso exige el permiso
|
|
183
|
+
# ssm:DescribeParameters, que no todo el mundo tiene.
|
|
184
|
+
def get_descriptions_by_prefix(ssm, prefix):
|
|
185
|
+
descripciones = {}
|
|
186
|
+
next_token = None
|
|
187
|
+
# Con prefijo "/" (backup de la cuenta entera) no se filtra: se piden todos
|
|
188
|
+
ruta = prefix.rstrip("/")
|
|
189
|
+
|
|
190
|
+
while True:
|
|
191
|
+
request_args = {"MaxResults": 50}
|
|
192
|
+
if ruta:
|
|
193
|
+
request_args["ParameterFilters"] = [
|
|
194
|
+
{"Key": "Path", "Option": "Recursive", "Values": [ruta]}
|
|
195
|
+
]
|
|
196
|
+
if next_token:
|
|
197
|
+
request_args["NextToken"] = next_token
|
|
198
|
+
|
|
199
|
+
try:
|
|
200
|
+
response = ssm.describe_parameters(**request_args)
|
|
201
|
+
except ClientError as e:
|
|
202
|
+
if _codigo_error(e) == "AccessDeniedException":
|
|
203
|
+
raise AccessDeniedError(prefix, sugerir_acotar=False) from None
|
|
204
|
+
raise
|
|
205
|
+
|
|
206
|
+
for meta in response.get("Parameters", []):
|
|
207
|
+
descripciones[meta["Name"]] = meta.get("Description", "")
|
|
208
|
+
|
|
209
|
+
next_token = response.get("NextToken")
|
|
210
|
+
if not next_token:
|
|
211
|
+
break
|
|
212
|
+
|
|
213
|
+
return descripciones
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
# Rellenar cada parámetro con su descripción. Devuelve la lista de avisos.
|
|
217
|
+
# Si no hay permisos, el campo NO se añade al fichero exportado: así al cargar se sabe
|
|
218
|
+
# que las descripciones no se gestionan y no se toca la que haya en AWS.
|
|
219
|
+
def agregar_descripciones_a_parametros(ssm, parameters, prefix):
|
|
220
|
+
try:
|
|
221
|
+
descripciones = get_descriptions_by_prefix(ssm, prefix)
|
|
222
|
+
except AccessDeniedError:
|
|
223
|
+
for param in parameters:
|
|
224
|
+
param.pop("parameter_description", None)
|
|
225
|
+
return [
|
|
226
|
+
"⚠ Sin permisos para leer las descripciones (hace falta ssm:DescribeParameters): "
|
|
227
|
+
"no aparecen en el fichero y al cargar no se tocarán las que ya existan en AWS."
|
|
228
|
+
]
|
|
229
|
+
|
|
230
|
+
for param in parameters:
|
|
231
|
+
param["parameter_description"] = descripciones.get(param["parameter_name"], "")
|
|
232
|
+
return []
|
|
233
|
+
|
|
234
|
+
|
|
177
235
|
# Comprobar si existe algún parámetro bajo un prefijo (None = no se pudo comprobar por permisos)
|
|
178
236
|
def existe_prefijo(ssm, prefix):
|
|
179
237
|
try:
|
|
@@ -318,7 +376,8 @@ def _orden_tags(param, tags_obligatorias):
|
|
|
318
376
|
|
|
319
377
|
|
|
320
378
|
# Función para exportar parámetros a un archivo
|
|
321
|
-
def export_parameters_to_file(parameters, file_path, tags_activas=False, tags_obligatorias=None
|
|
379
|
+
def export_parameters_to_file(parameters, file_path, tags_activas=False, tags_obligatorias=None,
|
|
380
|
+
incluir_tipo=False):
|
|
322
381
|
tags_obligatorias = tags_obligatorias or []
|
|
323
382
|
|
|
324
383
|
with open(file_path, 'w') as f:
|
|
@@ -329,6 +388,17 @@ def export_parameters_to_file(parameters, file_path, tags_activas=False, tags_ob
|
|
|
329
388
|
parameter_value = param['parameter_value']
|
|
330
389
|
f.write(f" {{'parameter_name': '{parameter_name}',\n")
|
|
331
390
|
|
|
391
|
+
# La descripción va debajo del nombre y antes del valor. Solo se escribe si
|
|
392
|
+
# se ha podido leer de AWS: si falta el campo, la carga no la toca.
|
|
393
|
+
if "parameter_description" in param:
|
|
394
|
+
descripcion = _escapar_valor_tag(param.get("parameter_description") or "")
|
|
395
|
+
f.write(f" 'parameter_description': \"{descripcion}\",\n")
|
|
396
|
+
|
|
397
|
+
# El tipo solo se escribe si se respeta (forzar_securestring = False): con
|
|
398
|
+
# el valor por defecto todo acaba en SecureString y el campo solo confundiría.
|
|
399
|
+
if incluir_tipo and "parameter_type" in param:
|
|
400
|
+
f.write(f" 'parameter_type': \"{param['parameter_type']}\",\n")
|
|
401
|
+
|
|
332
402
|
if not tags_activas:
|
|
333
403
|
# Usar triple comillas para valores largos
|
|
334
404
|
f.write(f" 'parameter_value': \"\"\"{parameter_value}\"\"\"}},\n\n")
|
|
@@ -346,6 +416,10 @@ def indexar_parametros(parametros):
|
|
|
346
416
|
return {
|
|
347
417
|
p['parameter_name']: {
|
|
348
418
|
"value": p.get('parameter_value', ""),
|
|
419
|
+
# None (campo ausente) significa "este fichero no gestiona descripciones",
|
|
420
|
+
# que no es lo mismo que una descripción vacía
|
|
421
|
+
"description": p.get('parameter_description'),
|
|
422
|
+
"type": p.get('parameter_type'),
|
|
349
423
|
"tags": extraer_tags(p),
|
|
350
424
|
}
|
|
351
425
|
for p in parametros
|
|
@@ -357,6 +431,10 @@ def indice_a_parametros(indice):
|
|
|
357
431
|
parametros = []
|
|
358
432
|
for nombre, datos in indice.items():
|
|
359
433
|
param = {"parameter_name": nombre, "parameter_value": datos["value"]}
|
|
434
|
+
if datos.get("description") is not None:
|
|
435
|
+
param["parameter_description"] = datos["description"]
|
|
436
|
+
if datos.get("type") is not None:
|
|
437
|
+
param["parameter_type"] = datos["type"]
|
|
360
438
|
for clave, valor in datos["tags"].items():
|
|
361
439
|
param[f"{TAG_FIELD_PREFIX}{clave}"] = valor
|
|
362
440
|
parametros.append(param)
|
|
@@ -416,24 +494,44 @@ def compare_parameters(file_path, backup_file_path, stdscr=None, tags_activas=Fa
|
|
|
416
494
|
"tipo": "Nuevo",
|
|
417
495
|
"detalle": "nuevo parámetro",
|
|
418
496
|
"value": actual["value"],
|
|
497
|
+
"description": actual["description"],
|
|
498
|
+
"type": actual["type"],
|
|
419
499
|
"tags": actual["tags"],
|
|
420
500
|
"value_changed": True,
|
|
501
|
+
"description_changed": actual["description"] is not None,
|
|
421
502
|
"tags_set": {k: v for k, v in actual["tags"].items() if str(v).strip()},
|
|
422
503
|
"tags_remove": [],
|
|
423
504
|
})
|
|
424
505
|
continue
|
|
425
506
|
|
|
426
507
|
value_changed = actual["value"] != anterior["value"]
|
|
508
|
+
# Solo se compara la descripción si los dos ficheros la traen: si falta en alguno,
|
|
509
|
+
# es que no se pudo leer de AWS y no se debe tocar
|
|
510
|
+
description_changed = (
|
|
511
|
+
actual["description"] is not None
|
|
512
|
+
and anterior["description"] is not None
|
|
513
|
+
and actual["description"] != anterior["description"]
|
|
514
|
+
)
|
|
515
|
+
type_changed = (
|
|
516
|
+
actual["type"] is not None
|
|
517
|
+
and anterior["type"] is not None
|
|
518
|
+
and actual["type"] != anterior["type"]
|
|
519
|
+
)
|
|
427
520
|
tags_set, tags_remove = ({}, [])
|
|
428
521
|
if tags_activas:
|
|
429
522
|
tags_set, tags_remove = _diff_tags(actual["tags"], anterior["tags"])
|
|
430
523
|
|
|
431
|
-
if not value_changed and not
|
|
524
|
+
if (not value_changed and not description_changed and not type_changed
|
|
525
|
+
and not tags_set and not tags_remove):
|
|
432
526
|
continue
|
|
433
527
|
|
|
434
528
|
detalles = []
|
|
435
529
|
if value_changed:
|
|
436
530
|
detalles.append("valor")
|
|
531
|
+
if description_changed:
|
|
532
|
+
detalles.append("descripción")
|
|
533
|
+
if type_changed:
|
|
534
|
+
detalles.append(f"tipo: {actual['type']}")
|
|
437
535
|
if tags_set:
|
|
438
536
|
detalles.append(f"tags: {', '.join(sorted(tags_set))}")
|
|
439
537
|
if tags_remove:
|
|
@@ -444,8 +542,12 @@ def compare_parameters(file_path, backup_file_path, stdscr=None, tags_activas=Fa
|
|
|
444
542
|
"tipo": "Modificado",
|
|
445
543
|
"detalle": " | ".join(detalles),
|
|
446
544
|
"value": actual["value"],
|
|
545
|
+
"description": actual["description"],
|
|
546
|
+
"type": actual["type"],
|
|
447
547
|
"tags": actual["tags"],
|
|
448
548
|
"value_changed": value_changed,
|
|
549
|
+
"description_changed": description_changed,
|
|
550
|
+
"type_changed": type_changed,
|
|
449
551
|
"tags_set": tags_set,
|
|
450
552
|
"tags_remove": tags_remove,
|
|
451
553
|
})
|
|
@@ -457,8 +559,12 @@ def compare_parameters(file_path, backup_file_path, stdscr=None, tags_activas=Fa
|
|
|
457
559
|
"tipo": "Eliminado",
|
|
458
560
|
"detalle": "se borrará de AWS",
|
|
459
561
|
"value": backup_dict[nombre]["value"],
|
|
562
|
+
"description": backup_dict[nombre]["description"],
|
|
563
|
+
"type": backup_dict[nombre]["type"],
|
|
460
564
|
"tags": backup_dict[nombre]["tags"],
|
|
461
565
|
"value_changed": False,
|
|
566
|
+
"description_changed": False,
|
|
567
|
+
"type_changed": False,
|
|
462
568
|
"tags_set": {},
|
|
463
569
|
"tags_remove": [],
|
|
464
570
|
})
|
|
@@ -15,6 +15,7 @@ from .funcions import (
|
|
|
15
15
|
compare_parameters, load_parameters, show_main_menu_selection,
|
|
16
16
|
AccessDeniedError, build_full_path, etiqueta_entrada, agregar_tags_a_parametros,
|
|
17
17
|
validar_tags_obligatorias, aplicar_cambios_tags, check_rds_correlacion,
|
|
18
|
+
agregar_descripciones_a_parametros,
|
|
18
19
|
show_report, prompt_input, indexar_parametros, indice_a_parametros,
|
|
19
20
|
ficheros_cargables,
|
|
20
21
|
POSICIONES_ENTORNO, CASES_ENTORNO, CASES_RUTA, MARCADOR_ENTORNO, slug_entrada,
|
|
@@ -51,7 +52,7 @@ que va siempre en minúscula, aplicándole el 'case_entorno' del perfil."""
|
|
|
51
52
|
# Opciones que viven fuera de 'configuraciones' y son opcionales: si el usuario no las
|
|
52
53
|
# tiene, se usa el valor de la plantilla. Se listan para poder decirle qué le falta.
|
|
53
54
|
CLAVES_OPCIONALES = (
|
|
54
|
-
"perfiles", "perfil_nuevos", "fichero_por_ruta",
|
|
55
|
+
"perfiles", "perfil_nuevos", "fichero_por_ruta", "forzar_securestring",
|
|
55
56
|
"tags_activas", "obligatorias_vacias", "tags_obligatorias",
|
|
56
57
|
)
|
|
57
58
|
|
|
@@ -98,6 +99,9 @@ def load_config():
|
|
|
98
99
|
config["fichero_por_ruta"] = bool(
|
|
99
100
|
getattr(modulo, "fichero_por_ruta", plantilla_config.fichero_por_ruta)
|
|
100
101
|
)
|
|
102
|
+
config["forzar_securestring"] = bool(
|
|
103
|
+
getattr(modulo, "forzar_securestring", plantilla_config.forzar_securestring)
|
|
104
|
+
)
|
|
101
105
|
# Si no se declara, los parámetros nuevos usan el primer perfil definido
|
|
102
106
|
# ('convencion_nuevos' es el nombre que tuvo en la 2.2.0)
|
|
103
107
|
primer_perfil = next(iter(config["perfiles"]), None)
|
|
@@ -301,9 +305,9 @@ def leer_ruta(stdscr, ssm, full_path, tags_activas, tags_obligatorias):
|
|
|
301
305
|
show_message(stdscr, f"{e}", 2)
|
|
302
306
|
return None, []
|
|
303
307
|
|
|
304
|
-
avisos =
|
|
308
|
+
avisos = agregar_descripciones_a_parametros(ssm, parameters, full_path)
|
|
305
309
|
if tags_activas:
|
|
306
|
-
avisos
|
|
310
|
+
avisos.extend(agregar_tags_a_parametros(ssm, parameters, tags_obligatorias))
|
|
307
311
|
|
|
308
312
|
return parameters, avisos
|
|
309
313
|
|
|
@@ -334,7 +338,7 @@ def crear_parametro(stdscr, ssm, entornos, perfil, nombre_perfil, tags_activas,
|
|
|
334
338
|
while True:
|
|
335
339
|
path = prompt_input(
|
|
336
340
|
stdscr,
|
|
337
|
-
"Crear nuevo parámetro (1/
|
|
341
|
+
"Crear nuevo parámetro (1/5): ruta",
|
|
338
342
|
f"Ruta del parámetro (perfil '{nombre_perfil}', sin el entorno):",
|
|
339
343
|
valor=path,
|
|
340
344
|
ayuda=ayudas.get(posicion, ""),
|
|
@@ -360,7 +364,7 @@ def crear_parametro(stdscr, ssm, entornos, perfil, nombre_perfil, tags_activas,
|
|
|
360
364
|
path = build_full_path(path_declarado, perfil, entorno)
|
|
361
365
|
|
|
362
366
|
if not show_report(
|
|
363
|
-
stdscr, "Crear nuevo parámetro (2/
|
|
367
|
+
stdscr, "Crear nuevo parámetro (2/5): confirmar la ruta",
|
|
364
368
|
[f"Perfil: {nombre_perfil}",
|
|
365
369
|
f"Escrita: {path_declarado}",
|
|
366
370
|
f"En AWS: {path}",
|
|
@@ -370,12 +374,26 @@ def crear_parametro(stdscr, ssm, entornos, perfil, nombre_perfil, tags_activas,
|
|
|
370
374
|
):
|
|
371
375
|
return
|
|
372
376
|
|
|
373
|
-
#
|
|
377
|
+
# 3. Descripción: va debajo del nombre y antes del valor, igual que en el fichero
|
|
378
|
+
descripcion = prompt_input(
|
|
379
|
+
stdscr,
|
|
380
|
+
"Crear nuevo parámetro (3/5): descripción",
|
|
381
|
+
"Descripción del parámetro (opcional):",
|
|
382
|
+
valor="",
|
|
383
|
+
permitir_vacio=True,
|
|
384
|
+
ayuda="Para qué sirve este parámetro. Se guarda en AWS y luego se puede editar "
|
|
385
|
+
"en el fichero exportado. Máximo 1024 caracteres; Enter para dejarla vacía.",
|
|
386
|
+
)
|
|
387
|
+
if descripcion is None:
|
|
388
|
+
return
|
|
389
|
+
descripcion = descripcion.strip()[:1024]
|
|
390
|
+
|
|
391
|
+
# 4. Valor (string plano o JSON)
|
|
374
392
|
valor = ""
|
|
375
393
|
while True:
|
|
376
394
|
valor = prompt_input(
|
|
377
395
|
stdscr,
|
|
378
|
-
"Crear nuevo parámetro (
|
|
396
|
+
"Crear nuevo parámetro (4/5): valor",
|
|
379
397
|
f"Valor para {path}:",
|
|
380
398
|
valor=valor,
|
|
381
399
|
ayuda='Texto plano o JSON en una línea, ej: {"host": "x", "user": "y", "pass": "z"}',
|
|
@@ -392,7 +410,7 @@ def crear_parametro(stdscr, ssm, entornos, perfil, nombre_perfil, tags_activas,
|
|
|
392
410
|
continue
|
|
393
411
|
break
|
|
394
412
|
|
|
395
|
-
#
|
|
413
|
+
# 5. Tags obligatorias
|
|
396
414
|
tags = {}
|
|
397
415
|
faltantes = []
|
|
398
416
|
if tags_activas:
|
|
@@ -406,7 +424,7 @@ def crear_parametro(stdscr, ssm, entornos, perfil, nombre_perfil, tags_activas,
|
|
|
406
424
|
predeterminado = entorno if clave == "Environment" else ""
|
|
407
425
|
respuesta = prompt_input(
|
|
408
426
|
stdscr,
|
|
409
|
-
f"Crear nuevo parámetro (
|
|
427
|
+
f"Crear nuevo parámetro (5/5): tags [{indice}/{len(tags_obligatorias)}]",
|
|
410
428
|
f"Valor de la tag obligatoria '{clave}':",
|
|
411
429
|
valor=predeterminado,
|
|
412
430
|
permitir_vacio=obligatorias_vacias,
|
|
@@ -433,7 +451,13 @@ def crear_parametro(stdscr, ssm, entornos, perfil, nombre_perfil, tags_activas,
|
|
|
433
451
|
aviso_rds = check_rds_correlacion(ssm, path)
|
|
434
452
|
|
|
435
453
|
# Resumen y confirmación
|
|
436
|
-
lineas = [
|
|
454
|
+
lineas = [
|
|
455
|
+
f"Ruta: {path}",
|
|
456
|
+
f"Descripción: {descripcion or '(vacía)'}",
|
|
457
|
+
f"Valor: {valor[:120]}",
|
|
458
|
+
"Tipo: SecureString",
|
|
459
|
+
"",
|
|
460
|
+
]
|
|
437
461
|
if tags_activas:
|
|
438
462
|
lineas.append("Tags:")
|
|
439
463
|
lineas.extend([f" {clave} = {valor_tag}" for clave, valor_tag in tags.items()])
|
|
@@ -456,6 +480,8 @@ def crear_parametro(stdscr, ssm, entornos, perfil, nombre_perfil, tags_activas,
|
|
|
456
480
|
"Type": "SecureString",
|
|
457
481
|
"Overwrite": False,
|
|
458
482
|
}
|
|
483
|
+
if descripcion:
|
|
484
|
+
kwargs["Description"] = descripcion
|
|
459
485
|
if tags_activas and tags:
|
|
460
486
|
kwargs["Tags"] = [{"Key": k, "Value": v} for k, v in tags.items()]
|
|
461
487
|
|
|
@@ -512,6 +538,7 @@ def main(stdscr, config=None):
|
|
|
512
538
|
TAGS_OBLIGATORIAS = config['tags_obligatorias']
|
|
513
539
|
OBLIGATORIAS_VACIAS = config.get('obligatorias_vacias', False)
|
|
514
540
|
FICHERO_POR_RUTA = config.get('fichero_por_ruta', False)
|
|
541
|
+
FORZAR_SECURESTRING = config.get('forzar_securestring', True)
|
|
515
542
|
PERFIL_NUEVOS = config.get('perfil_nuevos')
|
|
516
543
|
|
|
517
544
|
# Nombre del fichero exportado: con fichero_por_ruta cada ruta tiene el suyo, así que
|
|
@@ -565,10 +592,12 @@ def main(stdscr, config=None):
|
|
|
565
592
|
continue
|
|
566
593
|
|
|
567
594
|
# Exportar parámetros al archivo principal
|
|
568
|
-
export_parameters_to_file(parameters, file_name, TAGS_ACTIVAS, TAGS_OBLIGATORIAS
|
|
595
|
+
export_parameters_to_file(parameters, file_name, TAGS_ACTIVAS, TAGS_OBLIGATORIAS,
|
|
596
|
+
incluir_tipo=not FORZAR_SECURESTRING)
|
|
569
597
|
|
|
570
598
|
# Crear un respaldo exacto del archivo principal (valores y tags)
|
|
571
|
-
export_parameters_to_file(parameters, backup_file_name, TAGS_ACTIVAS, TAGS_OBLIGATORIAS
|
|
599
|
+
export_parameters_to_file(parameters, backup_file_name, TAGS_ACTIVAS, TAGS_OBLIGATORIAS,
|
|
600
|
+
incluir_tipo=not FORZAR_SECURESTRING)
|
|
572
601
|
|
|
573
602
|
# Confirmación de archivos creados
|
|
574
603
|
lineas = [
|
|
@@ -661,14 +690,30 @@ def main(stdscr, config=None):
|
|
|
661
690
|
)
|
|
662
691
|
|
|
663
692
|
try:
|
|
664
|
-
if change["value_changed"]
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
Name
|
|
668
|
-
Value
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
693
|
+
if (change["value_changed"] or change.get("description_changed")
|
|
694
|
+
or change.get("type_changed")):
|
|
695
|
+
kwargs_put = {
|
|
696
|
+
"Name": param_name,
|
|
697
|
+
"Value": change["value"],
|
|
698
|
+
"Overwrite": True,
|
|
699
|
+
}
|
|
700
|
+
# El 'Type' solo es obligatorio al CREAR: al actualizar, si no
|
|
701
|
+
# se manda, AWS conserva el que tuviera el parámetro. Por eso
|
|
702
|
+
# con forzar_securestring = False se omite en las
|
|
703
|
+
# modificaciones en vez de adivinarlo.
|
|
704
|
+
if FORZAR_SECURESTRING:
|
|
705
|
+
kwargs_put["Type"] = "SecureString"
|
|
706
|
+
elif change.get("type"):
|
|
707
|
+
kwargs_put["Type"] = change["type"]
|
|
708
|
+
elif tipo == "Nuevo":
|
|
709
|
+
kwargs_put["Type"] = "SecureString"
|
|
710
|
+
# put_parameter con Overwrite reescribe la definición del
|
|
711
|
+
# parámetro: si no se manda la descripción, la que hubiera en
|
|
712
|
+
# AWS se pierde. Así que se manda siempre que el fichero la
|
|
713
|
+
# traiga, incluso cuando lo que cambió fue solo el valor.
|
|
714
|
+
if change.get("description") is not None:
|
|
715
|
+
kwargs_put["Description"] = change["description"]
|
|
716
|
+
ssm.put_parameter(**kwargs_put)
|
|
672
717
|
# Los cambios de tags se aplican en la misma pasada que el valor
|
|
673
718
|
aplicar_cambios_tags(
|
|
674
719
|
ssm, param_name, change["tags_set"], change["tags_remove"]
|
|
@@ -715,7 +760,8 @@ def main(stdscr, config=None):
|
|
|
715
760
|
"tags": change["tags"],
|
|
716
761
|
}
|
|
717
762
|
export_parameters_to_file(
|
|
718
|
-
indice_a_parametros(indice), backup_file_name, TAGS_ACTIVAS,
|
|
763
|
+
indice_a_parametros(indice), backup_file_name, TAGS_ACTIVAS,
|
|
764
|
+
TAGS_OBLIGATORIAS, incluir_tipo=not FORZAR_SECURESTRING
|
|
719
765
|
)
|
|
720
766
|
|
|
721
767
|
lineas.extend(["", f"Errores ({len(errores)}):"])
|
|
@@ -765,6 +811,9 @@ def main(stdscr, config=None):
|
|
|
765
811
|
# Ruta sin parámetros para ese entorno: se ignora en el backup total
|
|
766
812
|
continue
|
|
767
813
|
|
|
814
|
+
avisos_totales.extend(
|
|
815
|
+
agregar_descripciones_a_parametros(ssm, parameters, full_path)
|
|
816
|
+
)
|
|
768
817
|
if TAGS_ACTIVAS:
|
|
769
818
|
avisos_totales.extend(
|
|
770
819
|
agregar_tags_a_parametros(ssm, parameters, TAGS_OBLIGATORIAS)
|
|
@@ -774,7 +823,8 @@ def main(stdscr, config=None):
|
|
|
774
823
|
# Crear archivo de backup total listado
|
|
775
824
|
backup_file_name = "total_listed_parameters_backup.py"
|
|
776
825
|
export_parameters_to_file(
|
|
777
|
-
all_parameters, backup_file_name, TAGS_ACTIVAS, TAGS_OBLIGATORIAS
|
|
826
|
+
all_parameters, backup_file_name, TAGS_ACTIVAS, TAGS_OBLIGATORIAS,
|
|
827
|
+
incluir_tipo=not FORZAR_SECURESTRING
|
|
778
828
|
)
|
|
779
829
|
|
|
780
830
|
# Confirmación de backup creado
|
|
@@ -796,7 +846,8 @@ def main(stdscr, config=None):
|
|
|
796
846
|
# Crear archivo de backup de todos los parámetros
|
|
797
847
|
backup_file_name = "all_parameters_backup.py"
|
|
798
848
|
export_parameters_to_file(
|
|
799
|
-
parameters, backup_file_name, TAGS_ACTIVAS, TAGS_OBLIGATORIAS
|
|
849
|
+
parameters, backup_file_name, TAGS_ACTIVAS, TAGS_OBLIGATORIAS,
|
|
850
|
+
incluir_tipo=not FORZAR_SECURESTRING
|
|
800
851
|
)
|
|
801
852
|
lineas = [
|
|
802
853
|
f"Backup de todos los parámetros creado: {backup_file_name}",
|
|
@@ -829,7 +880,8 @@ def main(stdscr, config=None):
|
|
|
829
880
|
# Crear archivo de backup con nombre claro
|
|
830
881
|
backup_file_name = f"{slug_entrada(selected_param)}_{selected_env}_backup.py"
|
|
831
882
|
export_parameters_to_file(
|
|
832
|
-
parameters, backup_file_name, TAGS_ACTIVAS, TAGS_OBLIGATORIAS
|
|
883
|
+
parameters, backup_file_name, TAGS_ACTIVAS, TAGS_OBLIGATORIAS,
|
|
884
|
+
incluir_tipo=not FORZAR_SECURESTRING
|
|
833
885
|
)
|
|
834
886
|
|
|
835
887
|
# Confirmación de backup creado
|
|
@@ -77,6 +77,16 @@ perfil_nuevos = "min"
|
|
|
77
77
|
# segunda lectura te machaque el fichero de la primera.
|
|
78
78
|
fichero_por_ruta = False
|
|
79
79
|
|
|
80
|
+
# ¿Todo lo que sube ParamsX va cifrado como SecureString?
|
|
81
|
+
# True (por defecto) -> sí, siempre. Es lo recomendable: los parámetros de una cuenta
|
|
82
|
+
# suelen tener secretos, y así no depende de acordarse.
|
|
83
|
+
# False -> se respeta el tipo que ya tuviera el parámetro en AWS (String, StringList o
|
|
84
|
+
# SecureString): al actualizar no se manda el tipo, así que AWS conserva el suyo.
|
|
85
|
+
# En este modo el fichero exportado trae además un campo 'parameter_type' que
|
|
86
|
+
# puedes editar si quieres cambiarlo a propósito. Los parámetros que crees con
|
|
87
|
+
# la opción 4 se siguen creando como SecureString.
|
|
88
|
+
forzar_securestring = True
|
|
89
|
+
|
|
80
90
|
|
|
81
91
|
## Configuraciones Tags
|
|
82
92
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: paramsx
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: Librería para gestionar y respaldar parámetros de AWS SSM de manera sencilla y eficiente.
|
|
5
5
|
Home-page: https://github.com/pistatxos/paramsx
|
|
6
6
|
Author: Mariox
|
|
@@ -70,7 +70,7 @@ Al ejecutar ```paramsx``` desde la terminal, accedes a un menú interactivo con
|
|
|
70
70
|
- Navega por la lista de parámetros configurada en tu archivo de configuración y selecciona cuál descargar.
|
|
71
71
|
- Elige el entorno deseado (por ejemplo, dev o prod).
|
|
72
72
|
- Archivos generados:
|
|
73
|
-
- parameters_dev.py o parameters_prod.py → Edita estos archivos para modificar, añadir o eliminar parámetros (valores y tags).
|
|
73
|
+
- parameters_dev.py o parameters_prod.py → Edita estos archivos para modificar, añadir o eliminar parámetros (descripciones, valores y tags).
|
|
74
74
|
- parameters_dev_backup.py → Respaldo automático del archivo descargado, incluidas las tags.
|
|
75
75
|
Los archivos se crean en la misma ruta desde donde ejecutas paramsx, evitando movernos innecesariamente entre carpetas.
|
|
76
76
|
- Si tu rol IAM no tiene permisos sobre la ruta configurada, verás un mensaje claro en lugar de una traza de boto3:
|
|
@@ -98,6 +98,7 @@ Esto te permitirá mantener respaldos seguros o realizar migraciones/reorganizac
|
|
|
98
98
|
4. Crear nuevo parámetro
|
|
99
99
|
- Crea un parámetro desde cero sin salir de la herramienta. Te pedirá, en este orden:
|
|
100
100
|
- La ruta **sin el entorno**, igual que la declaras en `parameter_list`: el perfil se encarga de colocarlo. Verás en pantalla la ruta resultante en AWS antes de seguir.
|
|
101
|
+
- La descripción, opcional: para qué sirve el parámetro. Se guarda en AWS y luego se puede editar en el fichero exportado.
|
|
101
102
|
- El valor: texto plano o JSON en una línea (por ejemplo `{"host": "x", "user": "y", "pass": "z"}`), que se valida antes de continuar.
|
|
102
103
|
- Las tags obligatorias, si tienes `tags_activas = True`.
|
|
103
104
|
- El perfil que se usa es el que declares en `perfil_nuevos`. Si tu perfil es de tipo `mixto`, escribe el `*` en la ruta para indicar dónde va el entorno.
|
|
@@ -212,6 +213,16 @@ perfil_nuevos = "min"
|
|
|
212
213
|
# segunda lectura te machaque el fichero de la primera.
|
|
213
214
|
fichero_por_ruta = False
|
|
214
215
|
|
|
216
|
+
# ¿Todo lo que sube ParamsX va cifrado como SecureString?
|
|
217
|
+
# True (por defecto) -> sí, siempre. Es lo recomendable: los parámetros de una cuenta
|
|
218
|
+
# suelen tener secretos, y así no depende de acordarse.
|
|
219
|
+
# False -> se respeta el tipo que ya tuviera el parámetro en AWS (String, StringList o
|
|
220
|
+
# SecureString): al actualizar no se manda el tipo, así que AWS conserva el suyo.
|
|
221
|
+
# En este modo el fichero exportado trae además un campo 'parameter_type' que
|
|
222
|
+
# puedes editar si quieres cambiarlo a propósito. Los parámetros que crees con
|
|
223
|
+
# la opción 4 se siguen creando como SecureString.
|
|
224
|
+
forzar_securestring = True
|
|
225
|
+
|
|
215
226
|
|
|
216
227
|
## Configuraciones Tags
|
|
217
228
|
|
|
@@ -269,6 +280,38 @@ En el nombre van el entorno, la ruta y el perfil, porque dos entradas pueden com
|
|
|
269
280
|
| `{"path": "/API/STA", "perfil": "max"}` | `parameters_dev__API_STA__max.py` |
|
|
270
281
|
| `{"path": "/API/*/STA", "perfil": "mixto_max"}` | `parameters_dev__API_env_STA__mixto_max.py` |
|
|
271
282
|
|
|
283
|
+
### La descripción de los parámetros
|
|
284
|
+
|
|
285
|
+
El fichero exportado trae un campo `parameter_description` **debajo del nombre y antes del valor**. Se lee de AWS, se edita como cualquier otro campo y se aplica al cargar:
|
|
286
|
+
|
|
287
|
+
```python
|
|
288
|
+
parametros = [
|
|
289
|
+
{'parameter_name': '/dev/api/sta/token',
|
|
290
|
+
'parameter_description': "Token JWT de la API, rota cada 90 dias",
|
|
291
|
+
'parameter_value': """abc123"""},
|
|
292
|
+
]
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Dos cosas que conviene saber:
|
|
296
|
+
|
|
297
|
+
- **Hace falta el permiso `ssm:DescribeParameters`.** La descripción no viaja en `GetParametersByPath` como el valor: es un metadato y se pide con `DescribeParameters`. Si tu rol no lo tiene, ParamsX te avisa, el campo **no aparece** en el fichero y al cargar no se toca la descripción que haya en AWS.
|
|
298
|
+
- **Al actualizar un parámetro, AWS reescribe su definición entera.** Por eso ParamsX manda siempre la descripción del fichero cuando cambia el valor, aunque no la hayas tocado: si no la mandara, se perdería. Y como no existe una API para cambiar solo la descripción, editarla crea una versión nueva del parámetro igual que cambiar el valor.
|
|
299
|
+
|
|
300
|
+
Un cambio de descripción se ve en la pantalla de confirmación como `Modificado (descripción)`, y si cambias las dos cosas, `Modificado (valor | descripción)`.
|
|
301
|
+
|
|
302
|
+
### Cifrado: `forzar_securestring`
|
|
303
|
+
|
|
304
|
+
ParamsX sube todo como **SecureString** por defecto, y así se recomienda dejarlo: los parámetros de una cuenta acaban teniendo secretos, y con esto no depende de que nadie se acuerde.
|
|
305
|
+
|
|
306
|
+
| `forzar_securestring` | Qué hace |
|
|
307
|
+
|---|---|
|
|
308
|
+
| `True` (por defecto) | Todo lo que se sube va cifrado como `SecureString`, sea del tipo que sea en AWS. |
|
|
309
|
+
| `False` | Se respeta el tipo que ya tuviera el parámetro (`String`, `StringList` o `SecureString`): al actualizar no se manda el tipo, y AWS conserva el suyo. El fichero exportado trae además un campo `parameter_type` que puedes editar si quieres cambiarlo a propósito, y ese cambio cuenta como uno más: verás `Modificado (tipo: SecureString)`. |
|
|
310
|
+
|
|
311
|
+
Los parámetros que creas con la opción 4 se crean siempre como `SecureString`, en los dos modos.
|
|
312
|
+
|
|
313
|
+
El tipo se lee de la misma llamada que el valor, así que **no hace falta ningún permiso extra** para esto (a diferencia de la descripción).
|
|
314
|
+
|
|
272
315
|
### Cómo acotar tu parameter_list (recomendación)
|
|
273
316
|
|
|
274
317
|
`parameter_list` es solo tu vista de trabajo: **la seguridad real la impone IAM, no este fichero**. La recomendación de uso es:
|
|
@@ -300,6 +343,7 @@ Formato del fichero exportado con `tags_activas = True`:
|
|
|
300
343
|
```python
|
|
301
344
|
parametros = [
|
|
302
345
|
{'parameter_name': '/dev/email/users',
|
|
346
|
+
'parameter_description': "Buzon que recibe los avisos de altas",
|
|
303
347
|
'parameter_value': """agpetrovici@stanalytics.es""",
|
|
304
348
|
'tagApplication': "",
|
|
305
349
|
'tagEnvironment': "",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2.2.2"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|