Sensitive data like passwords, API keys, and tokens require special handling to prevent accidental exposure in logs, state files, and terminal output.
🤖 AI-Generated Content
This documentation was generated with AI assistance and is still being audited. Some, or potentially a lot, of this information may be inaccurate. Learn more.
frompyvider.schemaimports_resource,a_str@classmethoddefget_schema(cls)->PvsSchema:returns_resource({"api_key":a_str(required=True,sensitive=True,# Marks as sensitivedescription="API authentication key"),"password":a_str(required=True,sensitive=True,description="Database password"),"username":a_str(required=True,description="Database username"# Not sensitive),})
Important: State files still contain sensitive values. Secure your state:
- Use remote state with encryption (S3 + encryption, Terraform Cloud)
- Never commit state files to version control
- Restrict access to state storage
frompyvider.resources.private_stateimportPrivateState@register_resource("mycloud_api_token")classApiTokenResource(BaseResource):asyncdef_create(self,ctx:ResourceContext,base_plan:dict):# Generate API tokenresult=awaitself.api.create_token()# Sensitive data goes in private state (encrypted)private_data={"access_token":result.access_token,"refresh_token":result.refresh_token,"secret_key":result.secret_key,}encrypted_private_state=PrivateState.encrypt(private_data)# Public data in regular statepublic_state={**base_plan,"token_id":result.id,"expires_at":result.expires_at,# NO sensitive data here}returnpublic_state,encrypted_private_stateasyncdefread(self,ctx:ResourceContext):# Access private state when neededifctx.private_state:private_data=PrivateState.decrypt(ctx.private_state)access_token=private_data["access_token"]# Use token for API calls
Private State Benefits:
- Encrypted in Terraform state
- Requires private_state_shared_secret in pyvider.toml
- Additional layer of security beyond sensitive attributes
# Good: All secrets marked sensitive"api_key":a_str(sensitive=True)"password":a_str(sensitive=True)"token":a_str(sensitive=True)# Bad: Secrets not marked"api_key":a_str()# Will appear in logs!
# Good: Use Terraform variablesvariable"api_key"{type=stringsensitive=true}resource"mycloud_api""main"{api_key=var.api_key}# Bad: Hardcoded secretsresource"mycloud_api""main"{api_key="hardcoded-key-123" # Never do this!}
# These are usually NOT sensitive"username":a_str()# Usernames often public"endpoint":a_str()# API endpoints are public"region":a_str()# Regions are public"instance_type":a_str()# Instance types are public"tags":a_list(a_str())# Tags usually public# These ARE sensitive"password":a_str(sensitive=True)"api_key":a_str(sensitive=True)"secret_token":a_str(sensitive=True)
Rule of thumb: If exposing the value could compromise security, mark it sensitive.
importpytest@pytest.fixturedefapi_key():"""Test API key from environment."""returnos.getenv("TEST_API_KEY","test-key-for-ci")asyncdeftest_api_authentication(api_key):config=ProviderConfig(api_endpoint="https://api.test.example.com",api_key=api_key# From fixture)provider=MyProvider()awaitprovider.configure(config)# Test without logging the keyassertprovider._configuredisTrue
# S3 with encryptionterraform{backend"s3"{bucket="my-terraform-state"key="prod/terraform.tfstate"region="us-west-2"encrypt=truekms_key_id="arn:aws:kms:..."}}
# pyvider.tomlprivate_state_shared_secret="your-encryption-key-here"# Or use environment variable# export PYVIDER_PRIVATE_STATE_SECRET="your-encryption-key-here"
Important: Keep this secret secure. If lost, private state cannot be decrypted.
# Good: Log with maskinglogger.debug("API request failed",endpoint=self.api_endpoint,key_prefix=self.api_key[:4]+"****"# Only show prefix)# Bad: Log full valuelogger.debug("API request failed",api_key=self.api_key)
Remember: Security is layered. Mark attributes as sensitive, use private state for highly sensitive data, secure your state storage, and never log secrets. When in doubt, mark it sensitive.